Reputation: 1
I have the following code:
va_list va[2];
va_start(va[0], fmt);
va_start(va[1], fmt);
process(fmt, va);
va_end(va[0]);
va_end(va[1]);
I've looked at various sites for documentation on va_start
and va_end
, and all they say is that va_end
should be invoked for each va_start
before the calling function returns.
What I am unsure about is whether or not the order of the calls is significant. In particular, is
va_end(va[0]);
va_end(va[1]);
sementically identical to
va_end(va[1]);
va_end(va[0]);
in the above sample code?
Upvotes: 9
Views: 602
Reputation:
The only relevant requirement in the C99 standard is:
7.15.1 Variable argument list access macros
1 [...] Each invocation of the
va_start
andva_copy
macros shall be matched by a corresponding invocation of theva_end
macro in the same function.
There is no requirement for the order of multiple va_end
invocations to match that of va_start
, or to match the reverse of that of va_start
, so implementations are required to accept either order.
You could even use a horrible mess like
void f(int a, ...) {
va_list ap;
goto b;
a:
va_end(ap);
return;
b:
va_start(ap, a);
goto a;
}
This matches all requirements of the standard, so implementations must accept it. As a result, even tricks where va_end
expands to something with unmatched braces are not allowed.
In practice, I am not even aware of any current implementation in which va_end
has any necessary effect at all. All the implementations that I've been able to find, at most set the value (or the first sub-value, depending on the type) to zero, which would make further use of va_arg
fail, but would not cause problems if you omit va_end
from your code. Most don't even do that. Now, I wouldn't actually remove it from code, since there are legitimate reasons why an implementation (current or future) may actually do something in its va_end
, but you can assume that current and future implementations will at least attempt to implement it in a way that matches the standard's requirements.
The historic implementations which use #define va_end(ap) }
are just that: history. They did not provide that macro in <stdarg.h>
, and they did not even have a <stdarg.h>
header. You shouldn't worry about them.
Upvotes: 6
Reputation: 1
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 ofva_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).
Upvotes: 1
Reputation: 297
Just call va_end once for each va_start, but you need to use va_arg to grab the individual arguments. Here's an example: http://www.cplusplus.com/reference/cstdarg/va_start/
Also, I don't think the ordering matters.
Upvotes: 2