What exactly is va_end for? Is it always necessary to call it?

C++CVariadic Functions

C++ 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYarikView Question on Stackoverflow
Solution 1 - C++greyfadeView Answer on Stackoverflow
Solution 2 - C++Maxim EgorushkinView Answer on Stackoverflow
Solution 3 - C++James CurranView Answer on Stackoverflow