El argumento va_list en realidad no es un va_list

8

Al intentar compilar este código

#include <stdarg.h>

void bar_ptr(int n, va_list *pvl) {
    // do va_arg stuff here
}

void bar(int n, va_list vl) {
    va_list *pvl = &vl; // error here
    bar_ptr(n, pvl);
}

void foo(int n, ...) {
    va_list vl;
    va_list *pvl = &vl; // fine here
    va_start(vl, n);
    bar(n, vl);
    va_end(vl);
}

int main() {
    foo(3, 1, 2, 3);
    return 0;
}

el compilador GCC imprime una advertencia sobre initialization from incompatible pointer typeen la barfunción. La declaración idéntica está bien en foo.

Parece que el tipo de un agumento de tipo va_listno es un va_list. Esto se puede probar fácilmente con una aserción estática como

_Static_assert(sizeof(vl) == sizeof(va_list), "invalid type");

en la barfunción Con GCC, los _Static_assertfracasos. Lo mismo se puede probar también en C ++ con declytpey std::is_same.

Me gustaría tomar la dirección del va_list vlargumento de bar, y pasarla como argumento de bar_ptr, para hacer pensar como la que se describe en este hilo . Por otro lado, está bien llamar bar_ptr(n, pvl)directamente desde main, reemplazar bar(n, vl).

De acuerdo con la nota 253 del borrador final del C11 ,

Está permitido crear un puntero a ay va_listpasar ese puntero a otra función

¿Por qué esto no se puede hacer si va_listse define como argumento de la función y no en el cuerpo de la función?

Solución alterna:

Incluso si esto no responde a la pregunta, una posible solución es cambiar el contenido barutilizando una copia local del argumento creado con va_copy:

void bar(int n, va_list vl) {
    va_list vl_copy;
    va_copy(vl_copy, vl);
    va_list *pvl = &vl_copy; // now fine here
    bar_ptr(n, pvl);
    va_end(va_copy);
}
Giovanni Cerretani
fuente
2
Con respecto a la cita de estándares que tiene: en realidad no pasa un puntero a un va_list.
Algún tipo programador
44
Está permitido crear un puntero a una va_list y pasar ese puntero a otra función No estás pasando un puntero a un va_list, estás pasando un real va_list.
Andrew Henle
Sé que estoy pasando a va_list. Supongamos que tengo una tercera función void bar_ptr(va_list *pvl);, quiero pasar un puntero va_list vla esa función. Editaré la pregunta para especificarla.
Giovanni Cerretani
1
Esta y esta pregunta podrían ser útiles. Y también es muy probable que el compilador tenga un manejo especial deva_list
algún tipo de programador
1
El va_copyenfoque que tiene es la solución canónica a este problema. Tengo algunas preguntas anteriores que básicamente concluyeron esto.
R .. GitHub DEJA DE AYUDAR A ICE

Respuestas:

4

va_listel estándar permite que sea una matriz, y a menudo lo es. Eso significa que va_listen una función el argumento se ajusta a un puntero al va_listprimer elemento interno que sea.

La extraña regla ( 7.16p3 ) con respecto a cómo va_listse pasa básicamente acomoda la posibilidad de que va_listsea ​​de un tipo de matriz o de un tipo regular.

Yo personalmente envolver va_listen una struct, así que no tengo que lidiar con esto.

Cuando luego pasa punteros a tal struct va_list_wrapper, es básicamente como si le pasara punteros va_list, y luego se aplica la nota al pie 253 que le da permiso para que tanto la persona que llama como la persona que llama manipulen lo mismo a va_listtravés de dicho puntero.

(Lo mismo se aplica desde jmp_bufy sigjmp_bufhacia setjmp.h. En general, este tipo de ajuste de matriz a puntero es una de las razones por typedeflas que es mejor evitar los tipos de matriz . Simplemente crea confusión, OMI).

PSkocik
fuente
2
Si va_listpuede ser una matriz, es engañoso que el estándar diga "El objeto apse puede pasar como argumento a otra función ..." (C 2018 7.16 3, apes un objeto de tipo va_list).
Eric Postpischil
2
@EricPostpischil: El estándar dice muchas cosas engañosas y las considera no-pero-/ WONTFIX siempre y cuando la intención sea clara para "alguien" ... :-P
R .. GitHub DEJA DE AYUDAR A ICE
2

Otra solución (solo C11 +):

_Generic(vl, va_list: &vl, default: (va_list *)vl)

Explicación: si vltiene tipo va_list, entonces va_listno es un tipo de matriz y simplemente tomar la dirección está bien para que va_list *apunte. De lo contrario, debe tener un tipo de matriz, y luego se le permite lanzar un puntero al primer elemento de la matriz (cualquiera que sea el tipo) a un puntero a la matriz.

R .. GitHub DEJA DE AYUDAR AL HIELO
fuente