va_arg, va_copy, va_end, va_start | |
Manage variable-argument lists
#include <stdarg.h>
void va_start ( va_list argptr , last_fixed_arg );
type va_arg ( va_list argptr , type );
void va_copy ( va_list dest , va_list src ); (C99)
void va_end ( va_list argptr );
The macros va_arg( ), va_start( ), and va_end( ) allow you to define C functions with variable numbers of arguments. Such functions use these macros to access the optional arguments, which are managed as anonymous objects in a list referenced by a pointer object of type va_list. The prototype syntax for a function that takes a variable number of arguments is as follows:
fn_type fn_name( [arg_type_1 fixed_arg_1, [arg_type_2 fixed_arg_2, [etc.]]]
last_arg_type last_fixed_arg, ... );
The ellipsis (...) after last_fixed_arg in this syntax is a literal ellipsis, which must appear in the function prototype to represent the optional arguments. A function with optional arguments must also take at least one mandatory argument. The ellipsis representing the optional arguments must be the last item in the parameter list, after the last mandatory parameter. The following example shows the prototype of the function vop( ), which takes two mandatory argumentsone with the type pointer to const char and one with the type intand a variable number of optional arguments:
double vop( const char * op, int argcount, ... );
In a function definition, the macros va_start( ), va_arg( ), va_copy( ), and va_end( ) allow you to access the optional arguments.
va_start
The macro va_start( ) initializes a va_list object so that it can be used in a va_arg( ) call to access the first optional argument in the variable-arguments list. The va_start( ) macro takes as its arguments the va_list object argptr, and the identifier of the last mandatory parameter, represented in this description by last_fixed_arg. Because va_start( ) makes certain assumptions about the alignment of the parameters in memory, last_fixed_arg must not be declared with the register storage class specifier, and must not have a function or array type, nor an integer type that is narrower than int.
Once you have called va_start( ), you can access the optional arguments in the list one by one through successive va_arg( ) calls. You must not call va_start( ) again for the same va_list object until you have passed it to the va_end( ) macro.
va_arg
To obtain each optional argument, call the va_arg( ) macro. The va_arg( ) macro takes as its arguments the va_list object argptr, and the type of the argument being read. Each such call to va_arg( ) returns one argument from the optional arguments list, and adjusts argptr so that the next call returns the next argument.
If there is no argument left to be read when you call va_arg( ), the behavior is undefined. The behavior is likewise undefined if the type indicated in the va_arg( ) call does not match the actual argument's type, with two exceptions: a signed integer type may match an unsigned integer type, if the value of the argument is representable in both types; and a pointer to void can match a pointer to char, signed char or unsigned char.
| The type argument to va_arg( ) must be in such a notation that appending an asterisk to it yields the type of a pointer to type. For example, the type argument may be char *, but not char [8], because char ** means "pointer to a pointer to char"; but char [8]* does not mean "pointer to an array of 8 char elements." (That type's name would be written char (*)[8].) |
|
va_copy
The macro va_copy( ) copies the state of the argument list referenced by the va_list object src to the object dest. If you have already called va_arg( ) for the same va_list object, then the copy produced by va_copy( ) is set to access the same argument as the original at the time it was copied. Otherwise, the copy is initialized to access the first argument in the list. You must call va_end( ) both for the copy and for the original after use.
va_end
To facilitate a clean return, a function that takes a variable number of arguments must call the va_end( ) macro after it has finished reading its optional arguments. The va_end( ) macro may render argptr unusable until it is re-initialized by calling va_start( ).
Example
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
double vproduct( int n, va_list argptr );
double vsum( int n, va_list argptr );
double vop( const char * op, int argcount, ... );
// main( ) calls vop( ) to perform calculations. vop( )'s arguments are:
// (1) the name of the operation ("sum", "product",
// "sum minus the product");
// (2) the number of operands;
// (3 through n) the operands themselves.
// Iterates through operations twice: once with three operands, once with
// six.
int main( )
{
double d1, d2, d3, d4, d5, d6;
puts( "Enter six floating-point numbers, please:" );
scanf( "%lf%lf%lf%lf%lf%lf", &d1, &d2, &d3, &d4, &d5, &d6 );
char *operation[ ] = { "sum", "product", "product minus the sum", NULL };
printf( "\nUsing the three numbers %lf, %lf, and %lf.\n", d1, d2, d3 );
for ( int i = 0; operation[i] != NULL; i++ )
{
printf( "The %s of these %d numbers is %lf\n",
operation[i], 3,
vop( operation[i], 3, d1, d2, d3 ) );
}
printf( "\nUsing six numbers:\n\t%lf \t%lf \t%lf \n\t%lf \t%lf \t%lf\n",
d1, d2, d3, d4, d5, d6 );
for ( int i = 0; operation[i] != NULL; i++ )
{
printf( "The %s of these %d numbers is %lf\n",
operation[i], 6,
vop( operation[i], 6, d1, d2, d3, d4, d5, d6 ) );
}
}
double vop( const char * op, int argcount, ... )
{
va_list argptr;
double result;
va_start( argptr, argcount );
if ( strcmp( op, "sum" ) == 0 )
result = vsum( argcount, argptr );
else if ( strcmp( op, "product" ) == 0 )
result = vproduct( argcount, argptr );
else if ( strcmp( op, "product minus the sum" ) == 0 )
{
va_list duplicate_argptr; // Clone the va_list in its present state.
va_copy( duplicate_argptr, argptr );
result = vproduct( argcount, argptr )
- vsum( argcount, duplicate_argptr );
va_end( duplicate_argptr ); // Clean up the clone.
}
else result = NAN;
va_end( argptr ); // Clean up the original.
return result;
}
double vproduct( int n, va_list argptr )
{
double product = 1.0;
for ( int i = 0; i < n; i ++ )
product *= va_arg( argptr, double );
return product;
}
double vsum( int n, va_list argptr )
{
double sum = 0.0;
for ( int i = 0; i < n; i ++ )
sum += va_arg( argptr, double );
return sum;
}
In this example, the helper functions vproduct( ) and vsum( ) take as their second argument a va_list object that has already been initialized, and also leave the cleaning up to the caller. In this respect, they are similar to the vprintf( ) and vscanf( ) function families.
See also the example for vfscanf( ) in this chapter.
See Also
vfprintf( ), vprintf( ), vsnprintf( ), and vsprintf( ); vfscanf( ), vscanf( ), and vsscanf( ); vfwprintf( ), vswprintf( ), and vwprintf( ); vfwscanf( ), vswscanf( ), and vwscanf( )
|