Use the <stdarg.h>
header (or, if you must, the older
<varargs.h>
).
Here is a function which concatenates an arbitrary number of
strings into malloc
'ed memory:
#include <stdlib.h> /* for malloc, NULL, size_t */ #include <stdarg.h> /* for va_ stuff */ #include <string.h> /* 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 <stdio.h>
rather than <stdlib.h>
, 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 .
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 <stdio.h> #include <stdarg.h> 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 <varargs.h>
package, instead of <stdarg.h>
,
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 .
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).
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.
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.)
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.)