Funciones variádicas

Una función variádica es una función que acepta varios parámetros. ¿Cuántos? Unos cuantos. Piensa en printf, que no se sabe cuántos parámetros acepta porque con cada llamada podemos pasar tantos parámetros como nos haga falta. En esta lección vemos cómo utilizar el tipo va_list y las macros va_start, va_arg y va_end que hay en stdarg.h para recorrer los argumentos variádicos de una función, y qué estrategias podemos seguir para detectar el final de los argumentos de una función variádica.

Las funciones variádicas en C son aquellas que nos permiten aceptar un número indefinido de parámetros, algo que puede resultar muy útil cuando no sabemos de antemano cuántos argumentos vamos a necesitar pasar a una función. Un ejemplo clásico que todos conocemos es printf, que puede recibir desde un solo parámetro hasta muchos más, dependiendo de cómo queramos formatear la salida.

Para declarar una función variádica en C, es imprescindible incluir el archivo de cabecera stdarg.h, pero solo en el lugar donde definamos la función, no donde la llamemos. Este archivo nos proporciona las macros necesarias para manejar los argumentos variables de forma segura y ordenada.

La sintaxis para declarar una función variádica requiere que haya al menos un parámetro fijo antes del indicador de parámetros variables, que es el triple punto .... Por ejemplo, si queremos crear una función llamada sumatorio que sume varios números, podríamos declararla así:

int sumatorio(int n, ...);

Aquí, n es el número fijo de parámetros que indica cuántos argumentos variables vamos a pasar después.

Dentro de la función, para manejar estos parámetros variables, primero declaramos una variable de tipo va_list, que será la que nos permita recorrer los argumentos. Luego, inicializamos esta variable con la macro va_start, pasando como segundo argumento el último parámetro fijo, en este caso n. Esto es necesario porque el compilador necesita saber dónde empiezan los argumentos variables en la pila.

Después, para acceder a cada argumento variable, usamos la macro va_arg, que recibe la lista de argumentos y el tipo de dato que esperamos obtener. Por ejemplo, si sabemos que los argumentos son enteros, podemos extraerlos así:

int valor = va_arg(vargs, int);

Finalmente, cuando terminamos de procesar los argumentos, debemos llamar a va_end para limpiar la lista.

Un ejemplo completo de la función sumatorio que suma n enteros pasados como argumentos variables podría ser:

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

int sumatorio(int n, ...) {
    va_list vargs;
    va_start(vargs, n);

    int acumulado = 0;
    for (int i = 0; i < n; i++) {
        int valor = va_arg(vargs, int);
        acumulado += valor;
    }

    va_end(vargs);
    return acumulado;
}

int main() {
    int total = sumatorio(3, 1, 2, 3);
    printf("Total es %d\n", total);
    return 0;
}

En este ejemplo, sumatorio recibe primero el número de argumentos variables que va a sumar, y luego esos argumentos. La función recorre cada uno, los suma y devuelve el resultado.

Es importante tener en cuenta que no existe un mecanismo automático para detectar el final de los argumentos variables. Por eso, es fundamental que el primer parámetro fijo nos indique cuántos argumentos variables vamos a procesar. Si intentamos leer más argumentos de los que realmente se pasaron, podemos acabar leyendo basura en memoria, lo que puede provocar comportamientos inesperados o errores.

Algunas implementaciones intentan usar un valor especial como terminador, por ejemplo un cero o un -1, para indicar el final de los argumentos variables. Sin embargo, esto no siempre es práctico ni seguro, porque esos valores podrían ser argumentos válidos. Por eso, la forma más fiable es siempre pasar explícitamente la cantidad de argumentos o usar un parámetro que codifique esa información, como hace printf con su cadena de formato.

En resumen, las funciones variádicas en C se manejan con tres macros principales: va_start para iniciar la lista de argumentos, va_arg para extraer cada argumento especificando su tipo, y va_end para finalizar el procesamiento. Aunque pueden parecer complicadas al principio, estas herramientas nos permiten crear funciones flexibles y potentes, siempre que tengamos cuidado con el manejo correcto del número y tipo de argumentos que recibimos.

Lista de reproducción
  1. 1
    Instalar CodeBlocks
    15 minutos
  2. 2
    Funciones y hola mundo
    17 minutos
  3. 3
    Variables y tipos de datos
    17 minutos
  4. 4
    Condicionales y operadores lógicos
    16 minutos
  5. 5
    Bucles
    11 minutos
  6. 6
    Punteros
    12 minutos
  7. 7
    Arrays
    14 minutos
  8. 8
    Estructuras
    12 minutos
  9. 9
    Otras construcciones de C
    9 minutos
  10. 10
    Memoria dinámica
    8 minutos
  11. 11
    El preprocesador (parte 1)
    10 minutos
  12. 12
    El preprocesador (parte 2)
    9 minutos
  13. 13
    Archivos de cabecera y múltiples .c (parte 1)
    8 minutos
  14. 14
    Archivos de cabecera y múltiples .c (parte 2)
    9 minutos
  15. 15
    C desde la línea de comandos (parte 1)
    8 minutos
  16. 16
    C desde la línea de comandos (parte 2)
    9 minutos
  17. 17
    Break y continue
    10 minutos
  18. 18
    Goto
    13 minutos
  19. 19
    Manipulación de bits
    15 minutos
  20. 20
    Máscaras de bit
    19 minutos
  21. 21
    Archivos (1): fopen y fclose
    13 minutos
  22. 22
    Archivos (2): leer con fgetc
    9 minutos
  23. 23
    Archivos (3): fseek y ftell
    11 minutos
  24. 24
    Archivos (4): leer con fgets
    9 minutos
  25. 25
    Archivos (5): fputc y fputs
    7 minutos
  26. 26
    Archivos (6): volcar en archivos con fwrite
    10 minutos
  27. 27
    Archivos (7): fread, fwrite y los arrays
    10 minutos
  28. 28
    Archivos (8): entrada estándar y salida estándar
    9 minutos
  29. 29
    Archivos (9): buffers
    14 minutos
  30. 30
    Archivos (y 10): otras funciones útiles con archivos
    5 minutos
  31. 31
    printf (1)
    18 minutos
  32. 32
    printf (parte 2)
    12 minutos
  33. 33
    scanf (parte 1)
    17 minutos
  34. 34
    scanf (parte 2)
    17 minutos
  35. 35
    fprintf, sprintf y snprintf
    8 minutos
  36. 36
    Tipos de datos opacos
    13 minutos
  37. 37
    Bibliotecas estáticas
    13 minutos
  38. 38
    Bibliotecas dinámicas
    15 minutos
  39. 39
    Más flags: i mayúscula (include), wall, werror, pedantic...
    12 minutos
  40. 40
    pkg-config
    12 minutos
  41. 41
    Make
    17 minutos
  42. 42
    GDB
    21 minutos
  43. 43
    Variables globales
    6 minutos
  44. 44
    extern
    9 minutos
  45. 45
    Funciones variádicas
    12 minutos
  46. 46
    El optimizador de GCC y la opción -O
    12 minutos
  47. 47
    Volatile
    6 minutos