Frontend Labs
⋯

22. Variadic functions and _Generic

22.1 Variadic functions (printf's mechanism)

#include <stdio.h>
#include <stdarg.h>

double sum(int count, ...) {
    va_list args;
    va_start(args, count);       // must start right after the last named parameter
    double total = 0;
    for (int i = 0; i < count; i++) {
        total += va_arg(args, double);  // caller and callee MUST agree on the type here —
                                         // there is no runtime check
    }
    va_end(args);
    return total;
}

int main(void) {
    printf("%.1f\n", sum(3, 1.0, 2.0, 3.0));  // 6.0
    return 0;
}

Because the compiler cannot check that the arguments you pass match the types va_arg requests, variadic functions are the other classic UB source in C after strings — this is exactly the mechanism behind Section 1.4's format-string mismatch bug.

22.2 _Generic: compile-time dispatch by type (C11)

#include <stdio.h>

#define print_value(x) _Generic((x),           \
    int: print_int,                             \
    double: print_double,                       \
    char *: print_string                        \
)(x)

void print_int(int x)       { printf("int: %d\n", x); }
void print_double(double x) { printf("double: %f\n", x); }
void print_string(char *x)  { printf("string: %s\n", x); }

int main(void) {
    print_value(42);        // "int: 42" — resolved at COMPILE time, zero runtime cost
    print_value(3.14);      // "double: 3.140000"
    print_value("hi");      // "string: hi"
    return 0;
}

_Generic is how <tgmath.h>'s sqrt/sin/cos pick the float vs. double vs. long double variant, and it's the closest C gets to function overloading — but it's resolved entirely at compile time by the static type of the expression, never at runtime, which is why it can't replace a real void*-based runtime-polymorphic API like qsort's.


Generated from real-life-c-guide.md · 8 Oct 2026 16:36