O argumento va_list na verdade não é um va_list

8

Ao tentar compilar esse 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;
}

o compilador GCC imprime um aviso sobre initialization from incompatible pointer typena barfunção. A afirmação idêntica está correta foo.

Parece que o tipo de um instrumento do tipo va_listnão é a va_list. Isso pode ser testado facilmente com uma asserção estática, como

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

na barfunção. Com o GCC, a _Static_assertfalha. O mesmo pode ser testado também em C ++ com declytpee std::is_same.

Gostaria de pegar o endereço do va_list vlargumento de bar, e passá-lo como argumento de bar_ptr, para fazer pensamentos como o descrito neste tópico . Por outro lado, não há problema em ligar bar_ptr(n, pvl)diretamente de main, substituindo bar(n, vl).

De acordo com a nota 253 da minuta final do C11 ,

É permitido criar um ponteiro para um va_liste passar esse ponteiro para outra função

Por que isso não pode ser feito se va_listdefinido como argumento da função, e não no corpo da função?

Gambiarra:

Mesmo que isso não responda à pergunta, uma possível solução alternativa é alterar o conteúdo barusando uma cópia local do argumento criado com 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
fonte
2
Com relação à cotação dos padrões, você tem: Na verdade, você não passa um ponteiro para a va_list.
Algum programador
4
É permitido criar um ponteiro para uma va_list e passar esse ponteiro para outra função. Você não está passando um ponteiro para a va_list, está passando um real va_list.
Andrew Henle
Eu sei que estou passando va_list. Suponha que eu tenho uma terceira função void bar_ptr(va_list *pvl);, quero passar um ponteiro va_list vlpara essa função. Vou editar a pergunta para especificá-la.
Giovanni Cerretani
1
Esta e esta pergunta podem ser úteis. E também é muito provável que o compilador tenha um tratamento especial deva_list
Some programador cara
1
A va_copyabordagem que você tem é a solução canônica para esse problema. Eu tenho algumas perguntas anteriores que basicamente concluíram isso.
R .. GitHub Pare de ajudar o gelo

Respostas:

4

va_listé permitido pelo padrão ser uma matriz, e geralmente é. Isso significa que, va_listem uma função, o argumento é ajustado a um ponteiro para o va_listprimeiro elemento interno de qualquer um .

A regra estranha ( 7.16p3 ) sobre como va_listé aprovada, basicamente acomoda a possibilidade de que va_listpossa ser de um tipo de matriz ou de um tipo regular.

Eu envolvo pessoalmente va_listum, structentão não tenho que lidar com isso.

Quando você passa os ponteiros para tal struct va_list_wrapper, é basicamente como se você tivesse passado os ponteiros para va_list, e então a nota de rodapé 253 se aplica, o que lhe dá a permissão para que um chamador e um chamador manipulem o mesmo va_listatravés desse ponteiro.

(A mesma coisa se aplica ao jmp_bufe sigjmp_bufa partir setjmp.h. Em geral, este tipo de matriz para ajuste de ponteiro é uma das razões pelas quais digitado de matriz typedefs devem ser evitados. Ele só cria confusão, IMO).

PSkocik
fonte
2
Se va_listpode ser uma matriz, é enganoso para o padrão dizer "O objeto appode ser passado como argumento para outra função ..." (C 2018 7.16 3, apé um objeto do tipo va_list).
Eric Postpischil 04/02
2
@EricPostpischil: O padrão diz muitas coisas enganosas e as considera não-mas-/ / WONTFIX, desde que a intenção seja clara para "alguém" ... :-P
R .. GitHub PARE DE AJUDAR O GELO
2

Outra solução (apenas C11 +):

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

Explicação: se vltem tipo va_list, então va_listnão é um tipo de matriz e apenas pegar o endereço é bom para va_list *apontar para ele. Caso contrário, ele deve ter o tipo de matriz e, em seguida, você poderá converter um ponteiro para o primeiro elemento da matriz (qualquer que seja o tipo) em um ponteiro para a matriz.

R .. GitHub PARE DE AJUDAR O GELO
fonte