6.15.2. Variable Argument Lists

Throughout most of the textbook, I use ellipses to denote code omitted for brevity. However, in the context of variable argument lists, the ellipses are part of the required syntax, denoting the variable number of arguments.

Typically, when a program calls a function, it must pass a fixed, predetermined number of arguments to it. In the case of default arguments, the function specifies values that are substituted for missing arguments, bringing the argument count up to the necessary number. Infrequently, programmers must create functions whose number of arguments is flexible or not predetermined at compile time - functions with a variable number of arguments. C++ inherits from C a mechanism, implemented with macros by the preprocessor, for creating such functions. To use the macros, we must understand the arguments in the function call and how the function's parameters and the macros work together in the function definition.

Function calls with a variable number of arguments begin with one fixed argument, and the corresponding function definitions begin with one fixed parameter followed by an ellipsis (this situation is the only one the text describes where the ellipses are part of the C++ syntax). There are two ways of using the fixed argument and parameter to implement a variable number of arguments:
  1. The fixed argument is an integer that specifies the number of variable arguments:
    function(5, "see", "the", "quick", "red", "fox");
  2. The fixed argument is a string literal that encodes the number and type of arguments (like the C printf function). Programmers may create their own encodings, but some common or "standard" encodings are:
    • d: decimal or integer (int, long, or short)
    • f: floating point (double or float)
    • c: character
    • s: string (i.e., a char* - C-strings are covered in chapter 8)
    function("dfcs", 10, 3.14, 'X', "Hello world");
The macros also use the address of the fixed argument to locate the subsequent arguments in memory using pointers and some tricky address arithmetic. Fortunately, the macros do the arithmetic, hiding the system's complexity from programmers.
Function calls with a variable number of arguments.

To create variable argument list functions, the program includes the enabling header file: <cstdarg> (C++) or <stdarg.h> (C). The header files specify four macros that work together to implement variable argument list functions:

va_list
The "data type" used to define a variable-length argument list. The macro creates an argument list that the program passes to the function at runtime.
va_start
Initializes the variables supporting the variable-length argument list. Requires two parameters:
  1. the variable-length argument created by va_list, and
  2. the name of the first argument.
va_end
Finish using the variable-length argument list; requires one parameter: the variable-length argument created by va_list
va_arg
Extracts an argument from the variable-length argument list. Requires two parameters:
  1. the variable-length argument created by va_list, and
  2. the "real" data type of the next argument. The following examples illustrate what I mean by "real" data type.
The variable argument list macros. It's necessary to implement the variable argument mechanism in the preprocessor with macros because it relies on textual information in the source code that is lost when the compiler component translates the source code to machine code (see The C++ compiler system).

Variable Argument List: Argument Number Example

#include <iostream>
#include <cstdarg>
using namespace std;

void average(int n, ...);

int main()
{
	average(5, 1, 2, 3, 4, 5);
	average(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

	return 0;
}


// Technique #1 -- the fixed argument specifies the number of arguments.

void average(int n, ...)			// the ... are part of the C++ syntax
{
	va_list	args;				// create a variable argument list
	double	sum = 0;

	va_start(args, n);			// begin accessing variable arguments

	for (int i = 0; i < n; i++)
		sum += va_arg(args, int);	// get an argument from the list
	cout << "Average: " << sum / n << endl;

	va_end(args);				// finish accessing variable arguments
}
Defining and calling a function with a variable number of arguments. The fixed arguments, the "5" and "10" highlighted in coral, denote the number of variable arguments following the fixed argument.

Variable Argument List: String Encoding Example

#include <iostream>
#include <cstdarg>
using namespace std;

void print(char* list, ...);

int main()
{
	print("fsfd", 3.144159, "Hello world", 2.7, 100);
	print("ss", "hello", "world");

	return 0;
}


// Technique #2 -- the fixed argument encodes the number and type of arguments.

void print(char* list, ...)
{
	va_list	args;

	va_start(args, list);

	char c;
	while(c = *list++)
		switch(c)
		{
			case 's' : cout << va_arg(args, char*) << endl;
				   break;
			case 'd' : cout << va_arg(args, int) << endl;
				   break;
			case 'f' : cout << va_arg(args, double) << endl;
				   break;
		}

	va_end(args);
}
Defining and calling a function with a string-encoded variable argument list. The fixed argument, highlighted in coral, in each function call encodes the number and type of each argument following the fixed argument. The examples follow the "standard" encoding list above. So, "fsfd" denotes four arguments that are (left to right) floating point, string, floating point, and decimal; "ss" denotes two strings. The example uses string literals throughout, but string variables are also allowed. (If you want to look ahead, please see C-strings.)