Skip to main content
A variadic parameter is a language feature in C that allows a function to accept an indefinite number of arguments of varying types. It is denoted by an ellipsis (...) in the function signature and relies on a set of macros defined in the <stdarg.h> standard library header to traverse the argument list at runtime.

Syntax and Mechanical Implementation

Because C does not inherently track the number or types of arguments passed to a variadic function, specific macros are used to manage the argument state. Historically (C89 through C17), a variadic function required at least one explicitly named parameter before the ellipsis to act as an anchor for the compiler. As of the C23 standard, a function can be declared solely with an ellipsis. The following complete program demonstrates the strict sequence of macro invocations required to safely extract arguments, including both universally compatible and C23-specific syntax.

Default Argument Promotions

A critical mechanical detail of C variadic parameters is default argument promotion. When arguments are passed through the ellipsis, the compiler automatically applies type promotions before passing them:
  1. Integer types smaller than int (e.g., char, short, _Bool) are promoted to int (or unsigned int).
  2. Floating-point types smaller than double (e.g., float) are promoted to double.
Consequently, passing a promoted type to va_arg is mandatory. Attempting to extract a char or float directly results in undefined behavior. The following program demonstrates the correct extraction of promoted types.

Memory and ABI Considerations

  • No Type Safety: va_arg performs no runtime type checking. If the type specified in va_arg does not match the actual type passed (post-promotion), the macro will misinterpret the memory or register contents, leading to corrupted data or undefined behavior.
  • No Boundary Checking: The <stdarg.h> macros cannot detect the end of the argument list. The caller and callee must establish a strict contract (such as a sentinel value, a format string, or an explicit count parameter) to prevent va_arg from reading out of bounds.
  • Pass-by-Value: Variadic arguments are always passed by value. To modify a variable from the caller’s scope, the caller must pass a pointer, and va_arg must extract it as a pointer type (e.g., va_arg(args, int*)).
  • State Duplication: If the argument list must be traversed multiple times, va_copy(va_list dest, va_list src) must be used to safely duplicate the state of an existing va_list. Both the original and the copy must independently be cleaned up with va_end.
Tired of Poor C Skills? Fix That With Deep Grasping!Learn More