Funciones con lista de argumentos variables en C

written by uve 26 May 2010

Hoy voy a explicar brevemente como podemos escribir una función en C con un número de parámetros variables. Esto no es muy habitual, pero en algunas ocasiones puede resultar interesante.

Como ejemplo vamos a implementar una función que concatene un número variable de cadenas de texto.

Para ello C nos ofrece una serie de macros (C89 y C99) incluidas en <stdarg.h>. Las macros son las siguientes:

  • void va_start(va_list ap, last) - Esta macro realiza la reserva de memoria de la lista de variables en ap. Con last se indica el nombre del último parámetro antes de la lista de variables, o lo que es lo mismo, el primer elemento de la lista será el parámetro siguiente a last.
  • type va_arg(va_list ap, type) - Esta macro actúa como un iterador. El parámetro ap indica la lista sobre la que se quiere iterar y type es el tipo de dato que se espera encontrar. Según la especificación oficial, utilizar va_arg después del último parámetro o con un tipo de dato que no coincida puede tener consecuencias impredecibles. Así que cuidado :)
  • void va_end(va_list ap) - Esta macro, finalmente, libera la memoria reservada en la lista ap.

Por último, comentar que también disponemos de un tipo de dato va_list, que realmente se define un "puntero" para recorrer la lista de parámetros.

Ahora veamos el código en acción:

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

char * concatenate(int numcad, ...)
{
   char * result = 0;
   char ** args = (char **)malloc(numcad*sizeof(char *));
   int size = 0, i;
   va_list ap;
   va_start(ap, numcad);
   for(i = 0; i < numcad; i++)
   {
      args[i] = va_arg(ap, char *);
      size += strlen(args[i]);
   }
   result = (char *)malloc((size+1)*sizeof(char)); // size+1 para el '\0'
   for(i = 0; i < numcad; i++)
      sprintf(result, "%s%s", result, args[i]);
   va_end(ap);
   return result;
}

Es obligatorio que la función reciba al menos un parámetro, ya que éste es necesario para construir la lista de parámetros (va_start). Para la implementación de la función he decidido que el primer parámetro sea un entero que me diga cuantas cadenas se van a concatenar, aunque esto no es obligatorio. De hecho, un claro ejemplo son las funciones de la familia printf o vprintf, donde el primer parámetro es una cadena con el formato que deseamos.

Una vez implementada la función podremos realizar distintas llamadas a concatenate con distintos parámetros:

int main()
{
    char * c;
    c = concatenate(2, "hola", " que tal");
    printf("%s\n", c);
    free(c);

    c = concatenate(3, "un ejemplo ", "algo mas", " largo");
    ...
    free(c);

Tags

La teoría es cuando crees saber algo, pero no funciona.
La práctica es cuando algo funciona, pero no sabes por qué.
Los programadores combinan la teoría y la práctica:
Nada funciona y no saben por qué.