Saturday, March 27, 2010

Funciones con un número indeterminado de argumentos en C

En el proyecto de fin de master utilicé listas de argumentos variables para poder pasar diferentes sistemas de ecuaciones diferenciales a la función con la que calculo exponentes de Lyapunov.

Como ese código es demasiado largo para ponerlo aquí, pondré las pequeñas pruebas que hice para aprender.

Se puede encontrar una explicación muy buena de las funciones variádicas (funciones que pueden aceptar un número indeterminado de argumentos) en un apartado del apéndice A del manual de The GNU C Library.
En este manual hay un ejemplo en el que se le pasa a una función un número indeterminado de enteros para que esta devuelva su suma.
Este es el ejemplo del manual con los comentarios traducidos y resaltando las líneas más importantes:
#include 
#include 

int suma (int count,...)
{
    va_list ap; //Lista de parámetros
    int i, sum;

    /* Inicializa la lista de argumentos */
    va_start (ap, count);         

    sum = 0;
    for (i = 0; i < count; i++)
    {
        /* Obtiene el siguiente argumento. */
        sum += va_arg (ap, int);    
    }
    
    /* Limpia la lista */
    va_end (ap);       
    
    return sum;
}

int main(void)
{
    /* Esta llamada imprime 16. */
    printf ("%d\n", suma (3, 5, 5, 6));

    /* Esta llamada imprime 19. */
    printf ("%d\n", suma (10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
    
    return (0);
}
En este ejemplo se debe destacar lo siguiente:
  • Para poder pasar un número indeterminado de argumentos se debe incluir la cabecera stdarg.h de la librería estándar de C.
  • En la lista de argumentos se usan tres puntos para indicar el comienzo de los argumentos variables.
  • Se debe declarar una variable de tipo va_list que se encargará de contener la lista de argumentos.
  • Se utiliza la macro va_start para inicializar la lista indicándole cuál es el último argumento fijo.
  • Se utiliza la macro va_arg para extraer los argumentos de la lista indicándole cuál es su tipo de datos. 
  • Se utiliza la macro va_end para limpiar la memoria.  
Este es el resultado de ejecutar el ejemplo anterior:
$ ./test1 
16
55
En este otro ejemplo usé el mismo procedimiento para pasar un número indeterminado de argumentos variables con tipos de datos definidos por el usuario:
#include  
#include 

typedef struct s_Fecha
{
    unsigned int anyo;
    unsigned int mes;
    unsigned int dia;
}t_Fecha;

t_Fecha setFecha(unsigned short dia, unsigned short mes,unsigned short anyo);
void imprimeFecha(t_Fecha fecha);

/* Imprime varias fechas */
void imprimeFechas(unsigned int numFechas, ...)
{
    va_list ap;
    int i;

    /* Se inicializa la lista de argumentos */
    va_start (ap, numFechas);         

    for (i = 0; i < numFechas; i++)
    {
        // Se saca el siguiente argumento de la lista
        imprimeFecha(va_arg (ap, t_Fecha));
    }
    
    printf("\n");
    
    //Se limpia la memoria
    va_end (ap);          
    
    return;
}

int main(void)
{
    t_Fecha fecha1, fecha2, fecha3;
    
    fecha1=setFecha(1,1,2009);
    fecha2=setFecha(2,2,2009);
    fecha3=setFecha(3,3,2009);
    
    /* Esta llamada imprime 1/1/2009 */
    imprimeFechas(1, fecha1);

    /* Esta llamada imprime 1/1/2009 2/2/2009*/
    imprimeFechas(2, fecha1, fecha2);
    
    /* Esta llamada imprime 1/1/2009 2/2/2009 3/3/2009*/
    imprimeFechas(3, fecha1, fecha2, fecha3);

    return (0);
}

/* Devuelve una variable de tipo t_Fecha */
t_Fecha setFecha(unsigned short dia, unsigned short mes,unsigned short anyo)
{
    t_Fecha fecha;
    
    fecha.dia=dia;
    fecha.mes=mes;
    fecha.anyo=anyo;
    
    return fecha;
}

/*Imprime una fecha*/
void imprimeFecha(t_Fecha fecha)
{
    printf("%d/%d/%d ",fecha.dia,fecha.mes,fecha.anyo);
    return;
}
Este es el resultado:
$ ./test2 
1/1/2009 
1/1/2009 2/2/2009 
1/1/2009 2/2/2009 3/3/2009 

No comments:

Post a Comment