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
- A variadic function needs at least one named parameter before the
....va_start(args, last_named)starts reading after it, eachva_arg(args, type)fetches the next extra argument as that type, andva_end(args)cleans up. - The function can't tell how many extras there are, or their types. Something must say: a count (like
average), a format string (likeprintf, where each%dor%smeans one more argument), or a special last value such asNULL. Reading more extras than were passed, or reading one as the wrong type, is undefined behavior. - Extras go through the default argument promotions:
charandshortarrive asint, andfloatarrives asdouble. So read them withva_arg(args, int)orva_arg(args, double);va_arg(args, float)is a bug. - To pass the extras on to another function, use the
vversions of the standard functions, which take ava_list:vprintf,vfprintf,vsnprintf. That's how every logging function is built. LOG(...)is a variadic macro:__VA_ARGS__pastes whatever arguments it was given.__LINE__is the line number where the macro is used (30 here), and__FILE__gives the file name, which is why logging macros use them.
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