¿Pasar va_list o apuntar a va_list?

6 minutos de lectura

Supongamos que tengo una función que toma argumentos variádicos (...) o un va_list pasado de otra función similar. La lógica principal está en esta función misma (llamémosla f1), pero quiero que pase el va_list a otra función (llamémosla f2) que determinará el siguiente tipo de argumento, obténgalo usando va_argy convertirlo y almacenarlo correctamente para que lo use la persona que llama.

¿Es suficiente pasar un va_list a f2, o es necesario pasar un puntero a va_list. A menos que se requiera que va_list sea un tipo de matriz o que almacene sus datos de posición en la ubicación va_list apunta al objeto (en lugar de en el objeto en sí), no puedo ver cómo pasarlo por valor podría permitir la función de llamada (f1) para ‘ver’ los cambios realizados por la función llamada va_arg.

¿Alguien puede arrojar luz sobre esto? Estoy interesado en lo que requiere el estándar, no en lo que permite alguna implementación en particular.

avatar de usuario
jeremyp

Parece que necesitará pasar un puntero a va_list. Para obtener más información, consulte el Documento estándar C99 sección 7.15. En particular, el punto 3 establece:

El objeto ap puede pasarse como argumento a otra función; si esa función invoca la macro va_arg con el parámetro ap, el valor de ap en la función de llamada es indeterminado y se pasará a la macro va_end antes de cualquier otra referencia a ap

[my italics]

Editar: Acabo de notar una nota al pie en el estándar:

215) Está permitido crear un puntero a va_list y pasar ese puntero a otra función, en cuyo caso la función original puede hacer un uso posterior de la lista original después de que la otra función regrese.

Entonces puede pasar un puntero a va_list y hacer va_arg(*va_list_pointer) en la función llamada.

  • De acuerdo con su última edición: un puntero es el camino a seguir (ya que el OP quiere los cambios en el va_list para ser definitivamente visible en la función de llamada).

    – café

    30 de julio de 2010 a las 8:48

  • @caf: No creo que debas haber editado mi referencia a vfprintf() porque parece que otros están cometiendo el mismo error que yo y sugiriendo que el interrogador mire los prototipos existentes (lo que en realidad no responde a su pregunta).

    – JeremyP

    30 de julio de 2010 a las 9:18

  • Diría que deje un comentario y / o vote hacia abajo entonces, parece mejor tener la respuesta correcta no ofuscada con la suposición incorrecta anterior …

    – café

    30 de julio de 2010 a las 12:05

Según tengo entendido, se supone que debes pasar el va_list directamente (no un puntero). Esto parece estar respaldado por comp.lang.c:

“Una va_list no es en sí misma una lista de argumentos variables; en realidad es una especie de puntero a uno. Es decir, una función que acepta una va_list no es en sí misma varargs, ni viceversa”.

  • No creo que esa cita respalde su afirmación. No tiene nada que prohíba pasar un puntero a un va_listy de hecho el estándar dice explícitamente que esto está permitido.

    – café

    30 de julio de 2010 a las 12:08

Encuentro los textos bastante ambiguos sobre esta cuestión. La más sencilla es quizás mirar en el estándar cómo funciona predefinido con va_list se supone que lo reciben, por ejemplo vsnprintf. Y esto es claramente por valor y no por referencia.

  • Las funciones estándar no están destinadas a llamarse de la manera que quiere el OP; después de llamarlas, el va_list ya no se puede usar y solo se debe pasar a va_end().

    – café

    30 de julio de 2010 a las 12:07

  • De hecho, soy el OP y estoy de acuerdo con caf en esto. JeremyP es el único que parece haber citado alguna evidencia y el estándar es bastante claro de que tienes que pasar un puntero para la semántica que quiero.

    – R.. GitHub DEJA DE AYUDAR A ICE

    30 de julio de 2010 a las 12:28

Debes pasar un puntero a un va_list si desea usarlo en una subfunción y luego no tener que pasarlo inmediatamente a va_end después. Desde C99:

Está permitido crear un puntero a un va_list y pasar ese puntero a otra función, en cuyo caso la función original puede hacer más uso de la lista original después de que la otra función regrese.

El estándar permite esto, sin embargo, en algunas plataformas de 64 bits donde va_list es un tipo de matriz, esto no funciona. Como la dirección de una matriz es la misma que la dirección del primer elemento de la matriz, pasar un puntero al va_list fallará al llamar va_arg con ese puntero a va_list como argumento.

Una forma de sortear esto es recibiendo el va_list como un nombre de argumento no convencional (generalmente con un sufijo con uno o más guiones bajos) y luego creando un nuevo local va_listal igual que:

#include <stdarg.h>

int vfoo(va_list ap_)
{
    int ret;
    va_list ap;
    va_copy(ap, ap_);
    ret = vbar(&ap);
    /* do other stuff with ap */
    va_end(ap);
    return ret;
}

Este es el enfoque que uso en mi vsnprintf implementación para llamar a otras funciones desde él para formatear.

Funciones en el pase de biblioteca C estándar va_list elemento en sí (man 3 vprintf):

   #include <stdarg.h>

   int vprintf(const char *format, va_list ap);
   int vfprintf(FILE *stream, const char *format, va_list ap);
   int vsprintf(char *str, const char *format, va_list ap);
   int vsnprintf(char *str, size_t size, const char *format, va_list ap);

  • Ninguna de estas funciones está destinada a ser llamada de la manera que quiere el OP; después de llamar a estas funciones, el va_list ya no se puede usar y solo se debe pasar a va_end().

    – café

    30 de julio de 2010 a las 12:06

avatar de usuario
terryyin

Pasar un puntero a va_list funciona bien en un sistema de 32 bits. Incluso puede obtener un parámetro a la vez en la subrutina. Pero no parece funcionar en un sistema de 64 bits, producirá una falla de segmento en va_arg().

  • Ninguna de estas funciones está destinada a ser llamada de la manera que quiere el OP; después de llamar a estas funciones, el va_list ya no se puede usar y solo se debe pasar a va_end().

    – café

    30 de julio de 2010 a las 12:06

¿Ha sido útil esta solución?