An example of use of varargs in C
Solution 1:
Remember that arguments are passed on the stack. The va_start
function contains the "magic" code to initialize the va_list
with the correct stack pointer. It must be passed the last named argument in the function declaration or it will not work.
What va_arg
does is use this saved stack pointer, and extract the correct amount of bytes for the type provided, and then modify ap
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 implemented as preprocessor macros. The actual implementation also depends on the compiler, as different compilers can have different layout of the stack and how it pushes arguments on the stack.
Solution 2:
But why it is not set to the beginning by default?
Maybe because of historical reasons from when compilers weren't smart enough. Maybe because you might have a varargs function prototype which doesn't actually care about the varargs and setting up varargs happens to be expensive on that particular system. Maybe because of more complex operations where you do va_copy
or maybe you want to restart working with the arguments multiple times and call va_start
multiple times.
The short version is: because the language standard says so.
Second, it is not clear to me why we need to give count as an argument. Can't C++ automatically determine the number of the arguments?
That's not what all that count
is. It is the last named argument to the function. va_start
needs it to figure out where the varargs are. Most likely this is for historical reasons on old compilers. I can't see why it couldn't be implemented differently today.
As the second part of your question: no, the compiler doesn't know how many arguments were sent to the function. It might not even be in the same compilation unit or even the same program and the compiler doesn't know how the function will be called. Imagine a library with a varargs function like printf
. When you compile your libc the compiler doesn't know when and how programs will call printf
. On most ABIs (ABI is the conventions for how functions are called, how arguments are passed, etc) there is no way to find out how many arguments a function call got. It's wasteful to include that information in a function call and it's almost never needed. So you need to have a way to tell the varargs function how many arguments it got. Accessing va_arg
beyond the number of arguments that were actually passed is undefined behavior.
Then it is not clear to me why do we use va_end(ap). What does it change?
On most architectures va_end
doesn't do anything relevant. But there are some architectures with complex argument passing semantics and va_start
could even potentially malloc memory then you'd need va_end
to free that memory.
The short version here is also: because the language standard says so.
Solution 3:
va_start initalises the list of variable arguments. You always pass the last named function argument as the second parameter. It is because you need to provide information about location in the stack, where variable arguments begin, since arguments are pushed on the stack and compiler cannot know where there's a beginning of variable argument list (there's no differentiation).
As to va_end, it is used to free resources allocated for the variable argument list during the va_start call.
Solution 4:
It's C macroses. va_start
sets internal pointer to address of first element. va_end
cleanup va_list
. If there is va_start
in code and there is no va_end
- it's UB.
The restrictions that ISO C places on the second parameter to the va_start() macro in header are different in this International Standard. The parameter parmN is the identifier of the rightmost parameter in the variable parameter list of the function definition (the one just before the ...). If the parameter parmN is declared with a function, array, or reference type, or with a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.