Variadic Functions
C provides a special mechanism for defining functions that take a
variable number of arguments. printf() and scanf() are typical examples
of these variadic functions. A variadic function must take at least one
named argument (usually, a format string), but the rest of the
arguments are optional. A special mechanism detects the number of
unnamed arguments and their types, as we will shortly see. A variadic
function is declared as follows:
void printf(const char * fmt,...);
The ellipsis indicates that the function may take any number of
arguments after the mandatory fmt. A variadic function must use C's
default calling convention. A function declared with the "pascal"
calling convention, for instance, cannot take a variable argument list.
Usage
Passing a different number of arguments of different types is
particularly useful in debugging and logging functions. Consider a
debugging function that displays a program's variables to the screen or
writes them to a file; such a function can be used to debug any
application. Normally, you would use printf() for this purpose;
however, because printf() doesn't support user-defined structs and
unions, writing a custom version may be more useful.
Implementation
First, we declare a variadic function that takes a format string and a
variable argument list:
void emit(const char *fmt,...);
For the sake of brevity, the format codes shall be 'd' for double
and 'i' for int. For example:
int ID;
double salary;
/*..*/
emit("is", ID, salary);
The standard header
traversing unnamed arguments. A variable of type va_list will refer to
each argument in turn. This variable is traditionally called ap
("argument pointer"). The second macro, va_start(), initializes ap to
point to the first unnamed argument. va_start() takes ap and the last
named argument of the variadic function:
va_start(ap, fmt); /*make ap point to the first arg after fmt*/
The third macro, va_arg(), takes ap and the current argument's type-
name. It returns the argument and advances ap to the next one (we rely
on the format string to detect the argument list's end). Finally, the
macro va_end(ap) must be called before the function returns to perform
the necessary cleanup. Here's the complete emit() function:
void emit(const char *fmt,...)
{
va_list ap /*will point to each unnamed argument in turn*/
int num;
double d;
const char *p=fmt;
va_start(ap,fmt); /* point to first element after fmt*/
while(*p)
{
if (*p=='i') /*int*/
{
num=va_arg(ap,int);
printf("%d",num);
}
else if (*p=='d') /*double*/
{
d=va_arg(ap,double);
printf("%f",d);
}
else
{
printf("unsupported format flag");
break;
}
++p;
}/*while*/
va_end(ap) /*cleanup*/
}
int main()
{
double salary=1000.00;
int id=1234;
emit("di",salary, id);
}