Search code examples
cundefined-behaviorvariadic-functions

C++ why does va_start expect the last non-variadic function argument?


I'm using Visual Studio 2012 to compile this sample code:

#include <stdarg.h>
#include <stdio.h>

const char * __cdecl foo(const char * format, const char * requiredArgument, ...)
{
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
    return requiredArgument;
}

int main(int, char **)
{
    foo("The %s is %d pixels wide and %d pixels high.", "box", 300, 200);
    return 0;
}

The debug build of the program terminates normally after printing the message "The box is 300 pixels wide and 200 pixels high.".

The release build crashes with a segmentation fault.

My interpretation for this behavior - but I may be wrong about that, please correct me if so - is that I'm incorrectly specifying a function parameter other than the last non-variadic one in va_start, the only admissible form being here va_start(args, requiredArgument) rather than va_start(args, format) as I would like to have. In other words, I'm misusing va_start in a way that makes the whole program flow unpredictable, and so the segmentation fault is nothing but fine here.

If my assumptions are right, I have two questions now:

  • Why is it even required to specify the last formally declared function parameter in va_start, if choosing anything else is apparently illegal?

  • Why does the picky VC++ compiler not raise a warning for such an easy to detect and potentially critical pitfall?


Solution

  • Why is it even required to specify the last formally declared function parameter in va_start, if choosing anything else is apparently illegal?

    Because that macro needs to know the address of the last argument.

    Why does the picky VC++ compiler not raise a warning for such an easy to detect and potentially critical pitfall?

    Because it's just not "intelligent" enough. Or its creators decided not to include this warning. Or maybe it could, but by default it's turned off and you can turn it on using some compiler flag.