c++cvariadic-functions

Does the ordering of multiple va_end calls matter?


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?


Solution

  • 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).