Variable Len Arg Lists
From: http://www.lysator.liu.se/c/c-faq/c-7.html
Section 7. Variable-Length Argument Lists
7.1: How can I write a function that takes a variable number of arguments?
Use the header (or, if you must, the older ).
Here is a function which concatenates an arbitrary number of strings into
malloc'ed memory:
#include /* for malloc, NULL, size_t */
#include /* for va_ stuff */
#include /* for strcat et al */
char *vstrcat(char *first, ...)
{
size_t len = 0;
char *retbuf;
va_list argp;
char *p;
if(first == NULL)
return NULL;
len = strlen(first);
va_start(argp, first);
while((p = va_arg(argp, char *)) != NULL)
len += strlen(p);
va_end(argp);
retbuf = malloc(len + 1); /* +1 for trailing \0 */
if(retbuf == NULL)
return NULL; /* error */
(void)strcpy(retbuf, first);
va_start(argp, first);
while((p = va_arg(argp, char *)) != NULL)
(void)strcat(retbuf, p);
va_end(argp);
return retbuf;
}
Usage is something like
char *str = vstrcat("Hello, ", "world!", (char *)NULL);
Note the cast on the last argument. (Also note that the caller must free
the returned, malloc'ed storage.)
Under a pre-ANSI compiler, rewrite the function definition without a
prototype ("char *vstrcat(first) char *first;{"), include rather than ,
add "extern char *malloc();", and use int instead of size_t. You may also
have to delete the (void) casts, and use the older varargs package instead
of stdarg. See the next question for hints.
Remember that in variable-length argument lists, function prototypes do not
supply parameter type information; therefore, default argument promotions
apply (see question 5.8), and null pointer arguments must be typed
explicitly (see question 1.2).
References: K&R II Sec. 7.3 p. 155, Sec. B7 p. 254; H&S Sec. 13.4 pp. 286-9; ANSI Secs. 4.8 through 4.8.1.3 .
7.2: How can I write a function that takes a format string and a variable
number of arguments, like printf, and passes them to printf to do most of
the work?
Use vprintf, vfprintf, or vsprintf.
Here is an "error" routine which prints an error message, preceded by the
string "error: " and terminated with a newline:
#include
#include
void
error(char *fmt, ...)
{
va_list argp;
fprintf(stderr, "error: ");
va_start(argp, fmt);
vfprintf(stderr, fmt, argp);
va_end(argp);
fprintf(stderr, "\n");
}
To use the older package, instead of , change the function header to:
void error(va_alist)
va_dcl
{
char *fmt;
change the va_start line to
va_start(argp);
and add the line
fmt = va_arg(argp, char *);
between the calls to va_start and vfprintf. (Note that there is no
semicolon after va_dcl.)
References: K&R II Sec. 8.3 p. 174, Sec. B1.2 p. 245; H&S Sec. 17.12 p. 337; ANSI Secs. 4.9.6.7, 4.9.6.8, 4.9.6.9 .
7.3: How can I discover how many arguments a function was actually called
with?
This information is not available to a portable program. Some old systems
provided a nonstandard nargs() function, but its use was always
questionable, since it typically returned the number of words passed, not
the number of arguments. (Structures and floating point values are usually
passed as several words.)
Any function which takes a variable number of arguments must be able to
determine from the arguments themselves how many of them there are. printf
-like functions do this by looking for formatting specifiers (%d and the
like) in the format string (which is why these functions fail badly if the
format string does not match the argument list). Another common technique
(useful when the arguments are all of the same type) is to use a sentinel
value (often 0, -1, or an appropriately-cast null pointer) at the end of the
list (see the execl and vstrcat examples under questions 1.2 and 7.1
above).
7.4: I can't get the va_arg macro to pull in an argument of type pointer-to
-function.
The type-rewriting games which the va_arg macro typically plays are stymied
by overly-complicated types such as pointer-to- function. If you use a
typedef for the function pointer type, however, all will be well.
References: ANSI Sec. 4.8.1.2 p. 124.
7.5: How can I write a function which takes a variable number of arguments
and passes them to some other function (which takes a variable number of
arguments)?
In general, you cannot. You must provide a version of that other function
which accepts a va_list pointer, as does vfprintf in the example above. If
the arguments must be passed directly as actual arguments (not indirectly
through a va_list pointer) to another function which is itself variadic (for
which you do not have the option of creating an alternate, va_list-accepting
version) no portable solution is possible. (The problem can be solved by
resorting to machine-specific assembly language.)
7.6: How can I call a function with an argument list built up at run time?
There is no guaranteed or portable way to do this. If you're curious, ask
this list's editor, who has a few wacky ideas you could try... (See also
question 16.11.) :x