[prev] [up] [overview] [next]

Section 7. Variable-Length Argument Lists

7.1: How can I write a function that takes a variable number of arguments?

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 .

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 <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 .

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.)


[prev] [up] [overview] [next]