Inicio > Apunte, C++, Independiente > Argumentos variables

Argumentos variables


Una de las herencias de C que fue bien acogida en C++ es el hecho de que una función tenga argumentos variables. Es decir, que el número de parámetros pueda variar y la función pueda reaccionar a este hecho como es debido.

Veamos un poquito de teoría. Todas las funciones, métodos, etc., tienen una dirección en memoria. El tamaño depende de la firma y el tipo de dato de regreso. La firma es el número de parámetros. Así, int foo(int a, int b); guardaría sizeof(int) bytes para el valor de retorno, el doble para los parámetros y otro tanto para la propia dirección de la función. De esta forma, los parámetros se albergan de forma contínua en memoria, al igual que un array. Entonces, conociendo yo la dirección en memoria de el primer parámetro, es fácil obtener el segundo:

int foo(int a, int b)
{
    int* p1;
    int* p2;

    p1 = &a;
    p2 = p1 + sizeof(int);
    cout << "Valor del segundo parámetro: " << *p2 << endl;
}

Este hecho es lo que hace posible que una función pueda tener parámetros variables: basta con que obtenga la dirección en memoria de la primera función para que, conociendo el tipo de parámetros que me envían, pueda convertir los bytes en el tipo de dato correspondiente.

Ok, basta de teoría. La librería estándar de C++ contiene –heredada de C– una serie de funciones que automatizan la tarea de manejar direcciones de memoria, sumarlas, etc. Veamos una pequeña referencia del tipo de dato y las funciones mencionadas.

  • va_list. Este tipo de dato va a albergar el puntero a las direcciones de memoria que contienen los argumentos. Usualmente es un typedef de un char*, ya que en esencia su utilidad es apuntar adecuadamente al búfer.
  • va_start. Esta función inicializa nuestra variable de tipo va_list. Toma dos parámetros. El primero es nuestra variable de tipo va_list. El segundo es el último parámetro conocido. Lo que hace esta función (o macro) es lo antes mencionado, toma la dirección de memoria del último parámetro conocido y posiciona el búfer pasada ésta dirección.
  • va_arg. Nos devuelve el valor del siguiente parámetro variable. Toma dos parámetros, el primero es nuestra variable va_arg, y el segundo es el tipo de dato que vamos a convertir (es decir, si el parámetro es un int, un double, etc; esto, para mover correctamente el puntero va_list y hacer el cast correspondiente).
  • va_end. Una vez que terminamos, hay que emplear esta macro para indicar que hemos terminado. El único parámetro es nuestro puntero va_list.

Es importante recalcar que es necesario saber el número de parámetros que se van a intentar obtener, de alguna forma, así como su tipo de dato. Por ejemplo, printfobtiene esta información del número de % que encuentre, así como el tipo de dato especificado (%d, %c, %s, etc). La función va_arg se puede llamar tantas veces como se requiera, pero hay que tener en cuenta que si nos pasamos podemos provocar algún comportamiento indefinido.

Bueno, en vivo y a todo color, un ejemplo.

int Suma(int cuenta, ...)
{
    va_list args;
    int total;

    va_start(args, cuenta);
    total = 0;

    for (int i = 0; i < cuenta; i++)
    {
        total += va_args(args, int);
    }
    va_end(args);

    return total;
}

int main()
{
    cout << "Suma 1+3+5+7 = " << Suma(4, 1, 3, 5, 7) << endl;
    cout << "Suma 1+2+3+4+5+6+7+8+9+10 = " <<
        Suma(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << endl;
    cout << "Suma 6+9+8+4+3+0 = " << Suma(6, 6, 9, 8, 4, 3, 0) 
        << endl;

    return EXIT_SUCCESS;
}

Ahora solo queda que juegues con esto y hagas funciones más complicadas.

Categorías:Apunte, C++, Independiente Etiquetas:
  1. Javier
    febrero 5, 2013 a las 5:22 pm

    Buenas Fernando, te comento que tienes un pequeño error en tu codigo de ejemplo. Cuando llamas a va_start lo haces con la variable num (la cual no esta definida) y no con cuenta.

    Solo eso, muy bueno el blog

  1. No trackbacks yet.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s