C/C++ Arena

Step 6 of 7

Variadic functions

printf takes one argument, or five, or twenty. Functions like that are variadic, and you can write your own with <stdarg.h>. The ... in the parameter list stands for "any number of extra arguments":

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

/* Average of the `count` ints that follow. */
double average(int count, ...) {
    va_list args;
    va_start(args, count);                    /* extras start after `count` */
    int total = 0;
    for (int i = 0; i < count; i++) {
        total += va_arg(args, int);           /* the next extra, read as an int */
    }
    va_end(args);
    return count > 0 ? (double)total / count : 0.0;
}

/* A printf-style logger: hand the format and the extras to vprintf. */
void log_line(int line, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    printf("[line %d] ", line);
    vprintf(fmt, args);
    printf("\n");
    va_end(args);
}

#define LOG(...) log_line(__LINE__, __VA_ARGS__)

int main(void) {
    printf("%.2f\n", average(3, 90, 72, 85));
    LOG("loaded %d maps in %.2f s", 7, 0.25);
    return 0;
}
82.33
[line 30] loaded 7 maps in 0.25 s

How it works

Why printf format mistakes are dangerous

This explains an earlier rule. printf finds out what each extra argument is only from the format string. Pass a double where the format says %d and printf reads the wrong bytes: undefined behavior. For printf itself, compilers check the format string against the arguments and warn, which is one more reason to read warnings. Your own variadic functions get no such check.

Your turn: write max_of(count, ...), returning the largest of count ints (count is at least 1), and format_into(buf, size, fmt, ...), which works exactly like snprintf: it formats into buf, never writing more than size bytes, and returns what snprintf would.

Previous: Command-line arguments Next: Challenge: a real command-line tool