Name | Description | Notes | Source | Availability | |||||||
---|---|---|---|---|---|---|---|---|---|---|---|
va_ |
Extract argument | L | M | (·) | <stdarg.h> |
C89 | C90 | C95 | C99 | C11 | |
va_ |
Copy argument iterator | L | M | (·) | <stdarg.h> |
C99 | C11 | ||||
va_ |
Release argument iterator | L | M | (·) | <stdarg.h> |
C89 | C90 | C95 | C99 | C11 | |
va_ |
Argument iterator | L | T | <stdarg.h> |
C89 | C90 | C95 | C99 | C11 | ||
va_ |
Initialise argument iterator | L | M | (·) | <stdarg.h> |
C89 | C90 | C95 | C99 | C11 |
In C, functions can be declared with only a partially specified parameter list, such as the following, which has two specified parameters, followed by an ellipsis:
void func(int a1, double a2, ...);
Calls to this function can then take any of the following forms, for example:
func(10, 20.0); func(10, 20.0, "fred"); func(10, 20.0, L"fred", 34); func(10, 20.0, 2.0 + I * 3, 10, 20, 30); // ad nauseum
The types of the first two arguments can be checked against the types of the first two parameters, but no further translation-time checking can be done on the remaining arguments.
<stdarg.h>
defines macros for accessing variable-length argument lists
from within a function defined with an ellipsis. It defines a
type va_
as an iterator to pass over the unspecified arguments in
turn. The types of these arguments can be determined at run
time, by inspecting another argument, for example.
#include<stdarg.h>
void va_start (va_list ap, parameter); void va_copy (va_list dst, va_list src); void va_end (va_list ap); type va_arg (va_list ap, type);
A va_
ap
must first be initialized using the
va_
macro, specifying the last named
parameter. Successive uses of va_
iterate over the unnamed arguments, indicating the argument
type expected at each stage. Finally, va_
must be applied to the iterator to clean up any resources
used. It must be used directly in the same block in which the
iterator was initialized by
va_
.
The type provided to va_
must match the argument type supplied at the current
position. The only exceptions are:
- when the two types are mutually equivalent signed and unsigned integer types and the argument is representable in both; and
- when the two types are
void *
and a pointer to a character type.
You also have to consider that arguments may be promoted
to unexpected types before becoming part of the list; see
Arithmetic promotions and Floating-point promotions.
Enumeration
types are especially unsuitable, as it is not possible to
know whether an enumeration type will be promoted to
int
or
long
, for
example.
_Promote
operator to select the correct type for
an enumeration.
va_
allows a new iterator dst
to be
initialized as a copy of an existing, and perhaps partially
used iterator src
. Such a copy can
then be passed to another function to be processed
separately. There is no way, however, to get the partially
iterated list back from the function.
Here's an example use of <stdarg.h>
:
#include<stdbool.h>
#include<stdarg.h>
#include<stdio.h>
#define DONE 0 #define INT 1 #define LONG 2 #define DOUBLE 3 void func(int t, ...) { va_list ap; va_start (ap, t); bool more = true; while (more) { int i; long l; double d; switch (t) { default: more = false; break; case INT: i = va_arg (ap, int); printf(" %d", i); break; case LONG: l = va_arg (ap, long); printf(" %ld", l); break; case DOUBLE: d = va_arg (ap, double); printf(" %d", d); break; } t = va_arg (ap, int); } va_end (ap); } func(DONE); func(DOUBLE, 3.4, INT, 10, LONG, 1009L, DONE);
The classic user of variable-length argument lists is
printf
and related functions. These
also come with v
-prefixed alternatives
that take a va_
as an argument, e.g., vprintf
. If you are thinking of
writing a function with a variable-length argument list, it's
a good idea to write a v
-prefixed
version of it first, then build the intended function from
it. For example:
void vfunc(int t, va_list ap) { bool more = true; while (more) { int i; long l; double d; switch (t) { default: more = false; break; case INT: i = va_arg (ap, int); printf(" %d", i); break; case LONG: l = va_arg (ap, long); printf(" %ld", l); break; case DOUBLE: d = va_arg (ap, double); printf(" %d", d); break; } t = va_arg (ap, int); } } void func(int t, ...) { va_list ap; va_start (ap, t); vfunc(t, ap); va_end (ap); }