stdarg.h provides the machinery for writing variadic functions — functions that accept a variable number of arguments, like printf. Without it you would have no portable way to iterate over the unnamed arguments. It defines va_list, and the va_start, va_arg, va_end, and va_copy macros.
#include <stdarg.h> #include <stdio.h> int sum(int count, ...) { va_list ap; va_start(ap, count); // initialise: 'count' is the last named param int total = 0; for (int i = 0; i < count; i++) total += va_arg(ap, int); // pull the next argument as int va_end(ap); // mandatory cleanup return total; } // sum(4, 10, 20, 30, 40) → 100
va_start(ap, last) initialises the list starting just after last (the final named parameter). va_arg(ap, type) reads the next argument and advances the cursor. va_end(ap) must be called before the function returns.
va_copy(dst, src) copies a va_list so you can traverse the arguments twice — useful when you call vsnprintf once to measure the required buffer length and again to fill it.
There is no type safety: va_arg trusts the type you give it. If the caller passed a double but you read it as int, you get undefined behaviour and likely garbage. printf encodes expected types in the format string; other variadic APIs use a sentinel value or an explicit count argument as the contract.
// compile: gcc -o vademo vademo.c // run: ./vademo // description: variadic minimum function using va_list #include <stdarg.h> #include <stdio.h> #include <limits.h> int vmin(int count, ...) { va_list ap; va_start(ap, count); int m = INT_MAX; for (int i = 0; i < count; i++) { int v = va_arg(ap, int); if (v < m) m = v; } va_end(ap); return m; } int main(void) { printf("min(3, 7, 1, 9) = %d\n", vmin(4, 3, 7, 1, 9)); printf("min(42) = %d\n", vmin(1, 42)); printf("min(empty) = %d\n", vmin(0)); // INT_MAX — no args return 0; }
Try passing a double where an int is expected by adding vmin(1, 3.14). On most platforms you will get a garbage result rather than a crash — that is the risk of variadic functions without runtime type checking. The count-based approach here is one way to bound the iteration; the other common approach is a NULL sentinel at the end of the argument list.