What exactly is va_end for? Is it always necessary to call it?
C++CVariadic FunctionsC++ Problem Overview
>va_end
- Macro to reset arg_ptr
.
After accessing a variable argument list, the arg_ptr
pointer is usually reset with va_end()
. I understand that it is required if you want to re-iterate the list, but is it really needed if you aren't going to? Is it just good practice, like the rule "always have a default:
in your switch
"?
C++ Solutions
Solution 1 - C++
va_end
is used to do cleanup. You don't want to smash the stack, do you?
From man va_start
:
> va_end() > > Each invocation of va_start() must be matched by a corresponding invocation of va_end() in the same function. After the call va_end(ap) the variable ap is undefined. Multiple traversals of the list, each bracketed by va_start() and va_end() are possible. va_end() may be a macro or a function.
Note the presence of the word must.
The stack could become corrupted because you don't know what va_start()
is doing. The va_*
macros are meant to be treated as black boxes. Every compiler on every platform can do whatever it wants there. It may do nothing, or it may do a lot.
Some ABIs pass the first few args in registers, and the remainder on the stack. A va_arg()
there may be more complicated. You can look up how a given implementation does varargs, which may be interesting, but in writing portable code you should treat them as opaque operations.
Solution 2 - C++
On Linux x86-64 only one traversal can be done over a va_list
variable. To do more traversals it has to be copied using va_copy
first. man va_copy
explains the details:
> va_copy() > > An obvious implementation would have a va_list be a pointer to the > stack frame of the variadic function. In such a setup (by far the most > common) there seems nothing against an assignment > > va_list aq = ap; > > Unfortunately, there are also systems that make it an array of pointers > (of length 1), and there one needs > > va_list aq; > *aq = *ap; > > Finally, on systems where arguments are passed in registers, it may be > necessary for va_start() to allocate memory, store the arguments there, > and also an indication of which argument is next, so that va_arg() can > step through the list. Now va_end() can free the allocated memory > again. To accommodate this situation, C99 adds a macro va_copy(), so > that the above assignment can be replaced by > > va_list aq; > va_copy(aq, ap); > ... > va_end(aq); > > Each invocation of va_copy() must be matched by a corresponding invoca‐ > tion of va_end() in the same function. Some systems that do not supply > va_copy() have __va_copy instead, since that was the name used in the > draft proposal.
Solution 3 - C++
In the common "parameters passed on the stack" implementation, I believe va_end() is usually nothing/empty/null. However, on platforms which have less traditional schemes, it becomes necessary. It's a "good practice" to include it to remain platform neutral.