Variadic Functions in C

Simple patterns.

Iterating over arguments

This is the typical template for a variadic function:

int add(int arg_count, ...) {
    int sum = 0;

    va_list args;
    va_start(args, arg_count);

    for (int i = 0; i < arg_count; i++) {
        sum += va_arg(args, int);
    }

    va_end(args);
    return sum;
}

int main(int argc, char** argv) {
    printf("1 + 2 + 3 = %d\n", add(3, 1, 2, 3));
    return 0;
}

Calling a printf–style library function

All the standard library's printf–style functions have a v–prefaced variant that takes a va_list argument.

void custom_printf(const char* format_string, ...) {
    va_list args;
    va_start(args, format_string);
    vprintf(format_string, args);
    va_end(args);
}

int main(int argc, char** argv) {
    custom_printf("%s and %s\n", "foo", "bar");
    return 0;
}

Chaining variadic function calls

You can't chain variadic functions directly — i.e. one variadic function can't call another — but you can chain them indirectly by passing a va_list argument.

int vadd(int arg_count, va_list args) {
    int sum = 0;

    for (int i = 0; i < arg_count; i++) {
        sum += va_arg(args, int);
    }

    return sum;
}

int add(int arg_count, ...) {
    va_list args;
    va_start(args, arg_count);

    int sum = vadd(arg_count, args);

    va_end(args);
    return sum;
}

int main(int argc, char** argv) {
    printf("1 + 2 + 3 = %d\n", add(3, 1, 2, 3));
    return 0;
}

This is a useful pattern — a v–prefaced function that takes a va_list argument and does the real work along with a variadic function that provides the default interface. (This is the way the standard library's printf–style functions are designed.)

Chaining calls to a printf–style library function

You can pass a va_list argument through multiple links of a chain.

void custom_vprintf(const char* format_string, va_list args) {
    vprintf(format_string, args);
}

void custom_printf(const char* format_string, ...) {
    va_list args;
    va_start(args, format_string);
    custom_vprintf(format_string, args);
    va_end(args);
}

int main(int argc, char** argv) {
    custom_printf("%s and %s\n", "foo", "bar");
    return 0;
}

Variadic macros

Variadic macros provide an alternative approach to chaining variadic function calls.

#define eprintf(...) fprintf(stderr, __VA_ARGS__)

All the tokens in the argument list after the last named argument become the 'variable argument' — this string is used to replace __VA_ARGS__ wherever it appears in the macro body.

At least one argument is required in place of the ellipsis in calls to the macro.