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

defines the necessary macros for 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); }
From CIO: 8 Free Online Courses to Grow Your Tech Skills
Join the discussion
Be the first to comment on this article. Our Commenting Policies