Skip to content

C Variable Arguments

C - Variable Arguments (Variadic Functions)

Section titled “C - Variable Arguments (Variadic Functions)”

Sometimes it’s useful to create functions that can accept a variable number of arguments, rather than a fixed number. Examples in the standard library include printf and scanf. These are called variadic functions.

C provides mechanisms in the <stdarg.h> header file to implement such functions.

A variadic function declaration must have at least one named parameter, followed by ellipses (...) as the last parameter.

return_type function_name(type fixed_param1, ...);

The fixed parameters are accessed normally. The ellipses represent the variable part of the argument list.

The <stdarg.h> header provides macros to handle the variable arguments:

  1. va_list: A type used to hold information about the variable argument list.
  2. va_start(va_list ap, last_fixed_param): Initializes the va_list variable (ap). last_fixed_param must be the name of the last named parameter before the ellipses. Must be called before accessing any variable arguments.
  3. va_arg(va_list ap, type): Retrieves the next argument from the list (ap) assuming it has the specified type. It also advances the internal pointer within ap to the subsequent argument. Crucially, the function must know the type of the next argument somehow (e.g., via a format string like printf, or a count passed as a fixed parameter).
  4. va_end(va_list ap): Cleans up the va_list. Must be called before the function returns, after all arguments have been processed.
  5. va_copy(va_list dest, va_list src): (C99) Creates a copy of the va_list state. Useful if you need to iterate through the arguments multiple times.

Let’s create a function that calculates the average of a variable number of integer arguments. We’ll pass the count of variable arguments as the first fixed parameter.

#include <stdio.h>
#include <stdarg.h> // Required for variadic functions
// Calculates the average of 'count' integer arguments that follow
double average(int count, ...) { // 'count' is the last fixed parameter
if (count <= 0) {
fprintf(stderr, "Error: Average requires at least one number.\n");
return 0.0; // Or handle error differently
}
va_list args; // 1. Declare a va_list variable
double sum = 0.0;
// 2. Initialize 'args' to point to the first variable argument
va_start(args, count);
// 3. Loop through and access each variable argument
for (int i = 0; i < count; i++) {
// Retrieve the next argument, assuming it's an int
int current_arg = va_arg(args, int);
sum += current_arg;
}
// 4. Clean up the va_list
va_end(args);
return sum / count;
}
int main(void) {
// Call average with different numbers of arguments
printf("Average of 2, 3, 4, 5 = %.2f\n", average(4, 2, 3, 4, 5));
printf("Average of 5, 10, 15 = %.2f\n", average(3, 5, 10, 15));
printf("Average of 100 = %.2f\n", average(1, 100));
printf("Average of nothing = %.2f\n", average(0)); // Handles error case
return 0;
}

Output:

Average of 2, 3, 4, 5 = 3.50
Average of 5, 10, 15 = 10.00
Average of 100 = 100.00
Error: Average requires at least one number.
Average of nothing = 0.00
  • Type Safety: Variadic functions bypass normal type checking for the variable arguments. The function must have a reliable way to determine the type of each argument it retrieves using va_arg. Passing arguments of the wrong type leads to undefined behavior.
  • Argument Promotion: Default argument promotions apply to variable arguments (e.g., float is promoted to double, char/short are promoted to int). You must use the promoted type in va_arg (e.g., use va_arg(ap, double) to retrieve an argument passed as a float).
  • Mechanism: The function needs a mechanism to know how many arguments were passed and their types (e.g., a count parameter, a format string, a sentinel value like NULL).

Variadic functions offer flexibility but require careful implementation to ensure correctness and avoid runtime errors.