Lección 16 · 16 de 20
Funciones variádicas · C
Escriba en C funciones con un número variable de argumentos usando stdarg.h: una media de n valores y un registro de mensajes al estilo de printf.

Una función variádica acepta un número variable de argumentos, como printf. Se declara con ... después de al menos un parámetro con nombre: double average(int count, ...). Las herramientas están en `stdarg.h`.
Dentro se declara un va_list y se inicia con `va_start(args, count)`, que recibe el último parámetro con nombre. Cada `va_arg(args, double)` devuelve el siguiente argumento con el tipo que se le indica, y `va_end` cierra la lista. Cada va_start tiene que llevar su va_end.
La función no tiene forma de saber cuántos argumentos le han llegado ni de qué tipo son: se lo tiene que decir quien llama. average lo sabe por count; printf, por los % del formato. Si no coinciden, como average(3, 7.5), se leería basura sin ningún aviso. Además, en los ... los float llegan convertidos a double y los char y short, a int; por eso va_arg pide double.
log_msg es un registro de mensajes al estilo de printf: escribe el nivel entre corchetes y pasa el formato y los demás argumentos a `vprintf`, la versión de printf que recibe un va_list. Existen también vfprintf, vsnprintf y compañía, pensadas precisamente para esto.
%% en el formato escribe un signo %, como en 91%.
Código · main.c
#include <stdarg.h>#include <stdio.h>// The first argument says how many numbers follow.double average(int count, ...) { va_list args; va_start(args, count); // start after the last named parameter double sum = 0; for (int i = 0; i < count; i++) sum += va_arg(args, double); // floats arrive as double va_end(args); // always pair va_start with va_end return count ? sum / count : 0;}// A printf-like logger: the format string says what the extra arguments are.void log_msg(const char *level, const char *format, ...) { va_list args; va_start(args, format); printf("[%-5s] ", level); vprintf(format, args); // the v... versions take a va_list printf("\n"); va_end(args);}int main(void) { printf("Average of 3: %.2f\n", average(3, 7.5, 9.0, 6.0)); printf("Average of 5: %.2f\n", average(5, 4.0, 8.0, 15.0, 16.0, 23.0)); printf("Average of 0: %.2f\n", average(0)); log_msg("INFO", "Server started on port %d", 8080); log_msg("WARN", "Disk %s is %d%% full", "/data", 91); log_msg("ERROR", "Order %s: %d items, total %.2f", "A-104", 3, 59.85); return 0;}Salida · cc main.c -o main && ./main
Average of 3: 7.50 Average of 5: 13.20 Average of 0: 0.00 [INFO ] Server started on port 8080 [WARN ] Disk /data is 91% full [ERROR] Order A-104: 3 items, total 59.85
Para recordar
...después de un parámetro con nombre.va_start,va_argyva_end, siempre en pareja inicio y fin.- Quien llama indica cuántos y de qué tipo son los argumentos.
- Los
floatllegan comodouble; loschar, comoint.
Ejercicio
Escriba int max_of(int count, ...), que devuelva el mayor de count enteros, y una variante de log_msg que escriba en un búfer con vsnprintf en vez de en la pantalla.