What Exactly Is Va_End For? Is It Always Necessary to Call It

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

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.

Is va_end required after a vsnprintf_s call?

va_end needs to be called for every va_start. From http://en.cppreference.com/w/c/variadic/va_end:

If there is no corresponding call to va_start or va_copy, or if va_end is not called before a function that calls va_start or va_copy returns, the behavior is undefined.

Not only do you need va_end but you also need to make sure that your function does not return before va_end is executed if va_start or va_copy is executed.

To answer your question - yes, this is a documentation bug in MSDN.

Is it okay to longjmp before calling va_end?

The C99 rationale explicitly states that va_start may allocate memory that ends up freed by va_end, exactly what you guessed in your question:

7.15.1.2 The va_copy macro

[...]

30 A much simpler approach is to copy the va_list object used to represent processing of the arguments. However, there is no safe way to
do this in C89 because the object may include pointers to memory allocated by the va_start macro and destroyed by the va_end macro.

The new va_copy macro provides this safe mechanism.

[...]

So yes, you need to invoke va_end before a longjmp. At the very least you'd otherwise have a memory leak on such an implementation.


Supposedly Pyramid OSx had an implementation where memory allocations were performed by va_start. Function arguments were passed in registers. This was the case even for variadic functions. It may have pre-dated ANSI C's invention of function prototypes, meaning the caller wouldn't know whether it was dealing with a variadic function. va_start allocated memory, presumably to store the function parameter values in a way that va_arg could easily access it. va_end freed the allocated memory.

Its implementation of va_start and va_end actually required matching va_start and va_end syntactically, because it was one that used unbalanced braces, so ANSI C already disallowed that implementation, but the same principle could be made to work while having matching braces.

I can find very little concrete information on this implementation, it's just bits and pieces on Usenet in the late '80s, early '90s. What little I did find may be incomplete or even just plain wrong. More details are very welcome, especially by anyone who used this implementation themselves.

Does va_copy() require va_end()?

Yes, every copy and the original, requires va_end() to be called. Your example is correct, the order of your va_end() calls can be reversed.

From ISO/IEC 9899:201x:

7.16.1.3 The va_end macro

The va_end macro facilitates a normal return from the function whose variable
argument list was referred to by the expansion of the va_start macro, or the function
containing the expansion of the va_copy macro, that initialized the va_list ap. The
va_end macro may modify ap so that it is no longer usable (without being reinitialized
by the va_start or va_copy macro). If there is no corresponding invocation of the
va_start or va_copy macro, or if the va_end macro is not invoked before the
return, the behavior is undefined.

The last sentence states that every va_start or va_copy must be accompanied by an va_end before returning.

Does the ordering of multiple va_end calls matter?

On some [old] implementations, va_start expanded to a left brace { followed by some declaration, and va_end expanded to a right brace } possibly preceded by some "finalization". Morally they should match. In practice, often but not always, order does not matter much (but in principle it does matter).

On recent GCC, these va_start and va_end macros expand to invocations of __builtin_va_start & __builtin_va_end so the compiler may care (perhaps in some future version) that these are correctly nested. See this. So the "good" order should be:

va_list va[2];
va_start(va[0], fmt);
va_start(va[1], fmt);
process(fmt, va);
va_end(va[1]);
va_end(va[0]);

In practice the order of va_end might not matter that much.

The indentation is mine to emphasize that va_start & va_end are "nesting"

Of course, you need to call va_arg to really retrieve variadic arguments (I hope that your process is doing that). stdarg(3) explains that well (for C code):

Each invocation of va_start() must be matched by a corresponding
invocation of va_end() in the same function.

Notice the corresponding word (emphasis is mine). I believe it means that va_start and va_end are really nesting (at least in principle).

Does `va_end()` need to be invoked before each `return` statement in a variadic function with multiple `return` statements?

From the C Standard (7.16.1 Variable argument list access macros)

1 ... Each invocation of the va_start and va_copy macros shall be matched
by a corresponding invocation of the va_end macro in the same
function.

So if va_start was used before any return statement and va_end was not yet invoked then it shall be invoked.

Calling a function with va_list argument needs va_start() at the beggining?

The question is if

int max(int n, va_list vals)

Called inside the function:

int max_first(int n, ...)

Needs a va_start(vals, n) invocation at the body's beginning?

No, it does not and it must not, the correct pattern is as follows:

int max_first(int n, ...) {
va_list vals;
va_start(vals, n);
int rv = max(n, vals);
va_end(vals);
return rv;
}

then

int max(int n, va_list vals) {
for (int i = 0; i < n; i ++) {
int val = va_arg(vals, int);
...
}

...
}

i.e. you can call va_start only in a function that has ... and you need to pass in the argument immediately preceding the ..., and each call to va_start must be always followed by va_end for the same value, and if you pass it to a function then you must call va_end right afterwards without using it in the calling function; if you want to process the arguments again, you must then call va_start again.

Can we call va_start() twice without calling va_end() in between?

C11 7.16.1/1:

[...] Each invocation of the va_start and va_copy macros shall be matched by a corresponding invocation of the va_end macro in the same function.

There's no corresponding va_end for both of the va_start calls so the code causes undefined behaviour, with no diagnostic required as the above quote is not part of a Constraint.

Can we call va_end without invoking va_start?

From C99 7.15.1.3/2 "The va_end macro":

If there is no corresponding invocation of the va_start or va_copy macro, or if the va_end macro is not invoked before the return, the behavior is undefined.



Related Topics



Leave a reply



Submit