Varargs example in C

Here I found an example of how varargs can be used in C.

#include <stdarg.h> double average(int count, ...) { va_list ap; int j; double tot = 0; va_start(ap, count); //Requires the last fixed parameter (to get the address) for(j=0; j<count; j++) tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument. va_end(ap); return tot/count; } 

I can only understand this example to some extent.

  • I donโ€™t understand why we use va_start(ap, count); . As I understand it, this way we set the iterator to our first element. But why is it not installed at the beginning by default?

  • I donโ€™t understand why we need to give count as an argument. Can C automatically determine the number of arguments?

  • I donโ€™t understand why we use va_end(ap) . What will it change? Did he set an iterator at the end of the list? But is it not set to the end of the list in a loop? Moreover, why do we need this? We no longer use ap ; why do we want to change it?

+24
c syntax variadic-functions
Apr 03 '13 at 10:20
source share
4 answers

Remember that arguments are passed on the stack. The va_start function contains the "magic" code to initialize va_list with the correct stack pointer. It must be passed in with the last named argument in the function declaration or will not work.

This va_arg uses this stored stack pointer and retrieves the correct number of bytes for the provided type, and then ap changes, so it points to the next argument on the stack.




In reality, these functions ( va_start , va_arg and va_end ) are not actually functions, but are implemented as preprocessor macros. The actual implementation also depends on the compiler, since different compilers may have different stackers and how they push the arguments on the stack.

+27
Apr 03 '13 at 10:25
source share

va_start initializes a list of variable arguments. You always pass the last argument to the function as the second parameter. This is because you need to provide information about the location on the stack where the variable arguments begin, since the arguments are pushed onto the stack, and the compiler cannot know where the beginning of the list of variable arguments is (there is no differentiation).

As for va_end, it is used to free resources allocated to the variable argument list during the va_start call.

+4
Apr 3 '13 at 10:25
source share

But why is it not installed by default?

Perhaps due to historical reasons due to which compilers are not smart enough. Perhaps because you might have a prototype of the varargs function that doesn't really care about varargs, and setting up varargs is expensive on this particular system. Perhaps because of the more complex operations in which you perform va_copy , or perhaps you need to restart the arguments several times and call va_start several times.

Short version: because the language speaks about it.

Secondly, it is not clear to me why we need to give an account as an argument. Can C ++ automatically determine the number of arguments?

It is not that all this is count . This is the last argument to the function. va_start needs to find out where varargs are. Most likely, this is due to the historical reasons for the old compilers. I do not understand why today it is impossible to implement in a different way.

As the second part of your question: no, the compiler does not know how many arguments were sent to this function. It may not even be in the same compilation unit or even in the same program, and the compiler does not know how the function will be called. Imagine a library with a varargs function, such as printf . When you compile your libc, the compiler does not know when and how programs will call printf . In most ABIs (ABIs are conventions on how functions are called, how arguments are passed, etc.), There is no way to know how many arguments a function call received. It is wasteful to include this information in a function call, and it is almost never needed. So you need to have a way to tell the varargs function how many arguments it received. Access to va_arg beyond the number of arguments that were actually passed is undefined behavior.

Then it is not clear to me why we use va_end (ap). What has he changed?

Most va_end architectures do not matter. But there are some architectures with complex semantics for passing arguments, and va_start can even potentially store malloc memory, then you need va_end to free this memory.

The short version is also here: because the language speaks about it.

+4
Apr 03 '13 at
source share

These are macros. va_start sets an internal pointer to the address of the first element. va_end clear va_list . If the code has va_start and no va_end , this is UB.

The restrictions that ISO C places in the second parameter in the va_start () macro in the header are different in this International Standard. The parmN parameter is the identifier of the rightmost parameter in the list of variable parameters of the function definition (the one immediately before ...). If a parmN parameter is declared using a function, array, or reference type or with a type that is incompatible with the type that occurs when passing an argument for which there is no parameter, the behavior is undefined.

+2
Apr 03 '13 at 10:25
source share



All Articles