É nula ** um tipo aceitável em ANSI-C?
Pergunta
Eu vi uma função cujo protótipo é:
int myfunc(void** ppt)
Esta função é chamada em um arquivo C como a = myfunc (mystruct ** var1);
onde mystruct é typedef para um de estrutura que temos.
Isso funciona sem quaisquer erros de compilação em MSVC6.0, Mas quando eu compilá-lo com algum outro compilador C, dá um erro no local onde esta função é chamada com mensagem de erro:
argumento do tipo mystruct ** é incompatível com parâmetro do tipo void **
O argumento de myfunc () é mantido como vazio ** porque parece ser uma espécie malloc genérica de função a ser chamada com vários tipos de estrutura variável para alocação de memória
- Existe algum tipo, como vazio ** permitido no padrão C / nenhum compiladores C?
- Como faço para corrigir isso? [I tentou lançar o argumento de chamada de função para
mystruct**
, mas não funcionou]
-AD
Solução
void**
é válido, mas, com base na sua mensagem de erro, você provavelmente terá que explicitamente lançar o argumento da seguinte forma:
mystruct **var1;
x = myfunc ((void**) var1);
Isso porque a função myfunc
está esperando o tipo void**
. Enquanto void*
pode ser implicitamente convertido para qualquer outro ponteiro, que não é assim para o ponteiro duplo - você precisa lançá-lo explicitamente
Outras dicas
comp.lang.c FAQ aborda esta questão em detalhe na Pergunta 4.9 . Em resumo, eles dizem não é estritamente portátil para lançar uma arbitrária ponteiro-para-ponteiro para uma void **
; eles vão a explicar que "código como este pode trabalhar e às vezes é recomendado, mas depende de todos os tipos de ponteiro que têm a mesma representação interna (que é comum, mas não universal)." Eles vão explicar que "qualquer valor void **
você joga com deve ser o endereço de um algum lugar valor void *
real; moldes como (void **)&dp
, embora possam fechar o compilador-se, são não portável (e não podem sequer fazer o que quiser)."
Assim, você pode seguramente / portably obter o comportamento desejado com um código como:
some_type *var1 = foo();
void *tmp_void_ptr = (void *)var1;
myfunc(&tmp_void_ptr);
Há uma razão para o compilador não pode converter automaticamente a partir mystruct**
para void**
.
Considere o seguinte código:
void stupid(struct mystruct **a, struct myotherstruct **b)
{
void **x = (void **)a;
*x = *b;
}
O compilador não vai reclamar sobre a conversão implícita de myotherstruct*
para void*
na linha *x = *b
, mesmo que essa linha está tentando colocar um ponteiro para uma myotherstruct
em um lugar onde apenas ponteiros para mystruct
deve ser colocado.
O erro é de fato na linha anterior, o que está convertendo "um ponteiro para um lugar onde ponteiros para mystruct
pode ser colocado" para "um ponteiro para um lugar onde ponteiros para qualquer pode ser colocar". Este é a razão não há nenhuma conversão implícita. Claro, quando você usa uma conversão explícita, o compilador assume que você sabe o que está fazendo.
Esta questão é um pouco confuso. Mas sim, void **
é certamente C legal e válido, e significa "ponteiro para ponteiro para void
" como esperado.
Eu não tenho certeza sobre o seu exemplo de uma chamada, os argumentos ( "mystruct ** VAR1") não fazem sentido. Se var1
é do tipo mystruct **
, a chamada deve apenas ler a = func(var1);
, isso pode ser um erro de digitação.
Fundição deve funcionar, mas você precisa elenco para void **
, que é o que os espera de função.
Como sujo como pode parecer:. Às vezes você não pode resolver um problema sem usar vazio **
Sim, void **
é perfeitamente aceitável e bastante útil em certas circunstâncias. Considere também que, dada a void **foo
declaração e void *bar
, o compilador vai saber o tamanho do objeto apontado por foo (ele aponta para um ponteiro, e todos os ponteiros são do mesmo tamanho, exceto em algumas plataformas antigas que você não precisa se preocupar) , mas não vai saber o tamanho do objeto apontado por bar (que poderia apontar para um objeto de qualquer tamanho). Você pode executar, portanto, com segurança ponteiro aritmética em ponteiros void **
mas não em ponteiros void *
. Você deve lançá-los para outra coisa em primeiro lugar, como um char *
. GCC irá permitir que você fingir que void *
e char *
são equivalentes, cada um apontando para um objeto um único byte em tamanho, mas isso não é padrão e não portável.