Conversão para void ** em diferentes compiladores

9

Eu tenho executado o seguinte código através de diferentes compiladores:

int main()
{
    float **a;
    void **b;
    b = a;
}

Pelo que pude reunir, nãovoid ** é um ponteiro genérico, o que significa que qualquer conversão de outro ponteiro não deve compilar ou, pelo menos, emitir um aviso. No entanto, aqui estão meus resultados (todos feitos no Windows):

  • gcc - lança um aviso, conforme o esperado.
  • g ++ - Lança um erro, conforme o esperado (isso ocorre devido à digitação menos permissiva de C ++, certo?)
  • MSVC (cl.exe) - Não envia nenhum aviso, mesmo com / Wall especificado.

Minha pergunta é: estou perdendo algo sobre a coisa toda e há algum motivo específico para o MSVC não produzir um aviso? MSVC faz produzir um aviso ao converter a partir void ** de float **.

Outra observação: se eu substituir a = bpela conversão explícita a = (void **)b, nenhum dos compiladores emitirá um aviso. Eu pensei que deveria ser um elenco inválido. Por que não haveria avisos?

O motivo pelo qual estou fazendo essa pergunta é porque estava começando a aprender o CUDA e no Guia de programação oficial ( https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory ) o seguinte código pode ser encontrado:

// Allocate vectors in device memory
float* d_A;
cudaMalloc(&d_A, size);

que deve realizar uma conversão implícita para void **para &d_A, como o primeiro argumento de cudaMallocfor do tipo void **. Código semelhante pode ser encontrado em toda a documentação. Isso é apenas um trabalho malfeito no final da NVIDIA ou, novamente, estou perdendo alguma coisa? Como nvccusa o MSVC, o código é compilado sem avisos.

CaptainProton42
fonte
3
Erros dos 3 postados ao vivo: godbolt.org/z/GQWMNo
Richard Critten
3
Os erros de código para mim com o MSVC. Qual versão você está usando? Mas sim, void**não é um ponteiro genérico. Somente void*é.
NathanOliver 21/01
Obrigado pela resposta rápida! 19.24.28315 para x64 aparentemente? Eu realmente não usei o MSVC antes.
CaptainProton42
2
(void**)é um elenco explícito no estilo c. Ele diz ao compilador para não olhar de perto o que você está fazendo e confiar em você. É uma substituição explícita do sistema de segurança de tipo e os compiladores são obrigados a aceitar basicamente qualquer tipo de conversão. Moldes no estilo C devem ser evitados, pois são poderosos demais. Use conversões em C ++ como as static_castque reclamarão se você estiver tentando fazer algo que não faz sentido.
François Andrieux 21/01
@RichardCritten idioma errado - sem erros godbolt.org/z/RmFpgN C ++ Cuda requer lançamentos explícitos, como de costume.
P__J__

Respostas:

4

Estou perdendo algo sobre a coisa toda e há algum motivo específico para o MSVC não produzir um aviso? O MSVC produz um aviso ao converter de void ** para float **

Essa atribuição sem uma conversão é uma violação de restrição; portanto, um compilador compatível com padrão imprimirá um aviso ou erro. No entanto, o MSVC não é totalmente compatível com a implementação C.

Outra observação: se eu substituir a = b pela conversão explícita a = (void **) b, nenhum dos compiladores emitirá um aviso. Eu pensei que este deveria ser um elenco inválido. Por que não haveria avisos?

Conversões de ponteiro via elenco são permitidas em algumas situações. O padrão C diz o seguinte na seção 6.3.2.3p7:

Um ponteiro para um tipo de objeto pode ser convertido em um ponteiro para um tipo de objeto diferente. Se o ponteiro resultante não estiver alinhado corretamente para o tipo referenciado, o comportamento será indefinido. Caso contrário, quando convertido novamente, o resultado será comparado ao ponteiro original. Quando um ponteiro para um objeto é convertido em um ponteiro para um tipo de caractere, o resultado aponta para o byte endereçado mais baixo do objeto. Incrementos sucessivos do resultado, até o tamanho do objeto, produzem ponteiros para os bytes restantes do objeto.

Assim, você pode converter entre os tipos de ponteiro, desde que não haja problemas de alinhamento e somente convertendo novamente (a menos que o destino seja a char *).

float* d_A;
cudaMalloc(&d_A, size);

...

Isso é apenas um trabalho malfeito no final da NVIDIA ou, novamente, estou perdendo alguma coisa?

Presumivelmente, essa função está desreferenciando o ponteiro fornecido e gravando o endereço de alguma memória alocada. Isso significaria que ele está tentando escrever para a float *como se fosse a void *. Não é o mesmo que a conversão típica de / para a void *. Estritamente falando, isso parece um comportamento indefinido, embora "funcione" porque os processadores x86 modernos (quando não estão no modo real) usam a mesma representação para todos os tipos de ponteiros.

dbush
fonte
@ dbush Muito informativo, obrigado! Entendo por que funciona se funciona. Ainda assim, a maioria dos compiladores não emitirá um aviso ou mesmo um erro porque &d_Anão possui o tipo necessário?
CaptainProton42 21/01
3
Tenha cuidado como você interpreta isso, não podem ser e são truques modelo entre se você compilação CUDA com um compilador C ++
talonmies