Using a pointer to const char as a second argument for va_start Using a pointer to const char as a second argument for va_start unix unix

Using a pointer to const char as a second argument for va_start


The second argument to va_start is the last "real" argument, before the var-args arguments begin.

The err_msg() function only has a single normal argument, the const char *fmt pointer, so then clearly that is the last argument before the var-args part and should be passed to va_start().

The manual page says:

The argument last is the name of the last argument before the variable argument list, that is, the last argument of which the calling function knows the type.

The actual type of the second argument to va_list doesn't matter.

Also note that the quite common printf() function very probably does exactly this, as it too only has a single "ordinary" argument, the format string.


From Wikipedia:

The macro va_start is then called with two arguments: the first is the variable declared of the type va_list, the second is the name of the last named parameter of the function.

Or from the C standard library:

The C library macro void va_start(va_list ap, last_arg) initializes ap variable to be used with the va_arg and va_end macros. The last_arg is the last known fixed argument being passed to the function ie. the argument before the ellipsis.

The point is that you need a standard argument before the ... to be able to give it to va_start. va_start will use it only to retrieve the supplementary arguments (pure C cooking).

What the last standard argument is is not relevant, but you may want to use it to know the number of supplementary arguments. Also this is an example of usage and I think that is what your source does: an example of usage.


Assuming you've quoted the book correctly, that statement:

The actual argument passed to this parameter will be the number of arguments represented by the ellipses section.

is incorrect, or at least misleading.

In your specific examples:

void f1(int n, ...);int f2(const char * s, int k, ...);

the last named parameter before the ellipsis happens to be an int whose value presumably is intended to specify the number of arguments corresponding to the ....

That's a reasonable convention (though you still need to specify the types of those arguments), but it's not required by the language.

The second argument to va_start() is always the name (not the value) of the last named parameter. Since va_start is a macro, not a function, the usual rule that all arguments are passed by value does not apply. Internally, va_start uses the parameter name to determine where in memory (or in registers) it can find the additional argument values. (There is no portable way to do this, so the actual definition of va_start is not specified by the standard; it has to be customized for each compiler.)

All that's really required is that the function must be able to figure out somehow the number and types of the arguments passed by the caller. printf does this by parsing the format string. execl() (a POSIX function, not defined by ISO C) does it by assuming that all the arguments are of type char*, and collecting arguments until it finds a null pointer. Other mechanisms are possible. And if the caller passes inconsistent arguments, the behavior is undefined; there's no way for the caller to detect an error.

Even in your example, the second argument to va_start is not "the number of arguments represented by the ellipses section". It's the name of a parameter. Even if the parameter value happens to be, say, 3, passing a literal 3 to va_start would be incorrect. The function will determine the number of arguments simply by reading the value of the named parameter, not by anything related to va_start.

The semantics of the va_start macro, and other items defined in <stdarg.h> are described in the C standard, section 7.16.