Conversione a vuoto ** su diversi compilatori


9

Ho eseguito il seguente codice attraverso diversi compilatori:

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

Da quello che sono stato in grado di raccogliere, nonvoid ** è un puntatore generico, il che significa che qualsiasi conversione da un altro puntatore non dovrebbe compilare o almeno lanciare un avviso. Tuttavia, ecco i miei risultati (tutto fatto su Windows):

  • gcc - Genera un avviso, come previsto.
  • g ++ : genera un errore, come previsto (ciò è dovuto alla tipizzazione meno permissiva di C ++, giusto?)
  • MSVC (cl.exe) - Non genera alcun avviso, anche con / Wall specificato.

La mia domanda è: mi sto perdendo qualcosa sull'intera faccenda e c'è qualche motivo specifico per cui MSVC non produce un avviso? MSVC fa produrre un avviso durante la conversione da void ** a float **.

Un'altra cosa da notare: se sostituisco a = bcon la conversione esplicita a = (void **)b, nessuno dei compilatori lancia un avvertimento. Ho pensato che questo dovesse essere un cast non valido, quindi perché non ci dovrebbero essere avvisi?

Il motivo per cui sto ponendo questa domanda è perché stavo iniziando a studiare CUDA e nella Guida di programmazione ufficiale ( https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory ) è possibile trovare il seguente codice:

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

che dovrebbe eseguire una conversione implicita in void **per &d_A, poiché il primo argomento di cudaMallocè di tipo void **. Codice simile può essere trovato in tutta la documentazione. Questo è solo un lavoro sciatto da parte di NVIDIA o, di nuovo, mi manca qualcosa? Poiché nvccutilizza MSVC, il codice viene compilato senza avvisi.


3
Errori dal 3 pubblicato dal vivo: godbolt.org/z/GQWMNo
Richard Critten

3
Gli errori di codice per me con MSVC. Che versione stai usando? Ma sì, void**non è un puntatore generico. Lo void*è solo .
NathanOliver,

Grazie per la risposta rapida! 19.24.28315 per x64 apparentemente? Non ho mai usato MSVC prima.
CaptainProton42,

2
(void**)è un cast esplicito in stile c. Dice al compilatore di non guardare da vicino ciò che stai facendo e di fidarti di te. È una sostituzione esplicita del sistema di sicurezza dei tipi e i compilatori sono tenuti ad accettare praticamente qualsiasi tipo di conversione. I cast in stile C dovrebbero essere evitati, sono troppo potenti. Usa cast di C ++ come i static_castquali ti lamenterai se stai cercando di fare qualcosa che non ha senso.
François Andrieux, il

@RichardCritten language errato - nessun errore godbolt.org/z/RmFpgN C ++ cuda richiede cast espliciti come al solito.
P__J__

Risposte:


4

Mi sto perdendo qualcosa sull'intera faccenda e c'è qualche motivo specifico per cui MSVC non genera un avviso? MSVC genera un avviso durante la conversione da void ** a float **

Questa assegnazione senza cast è una violazione del vincolo, quindi un compilatore conforme standard stamperà un avviso o un errore. Tuttavia, MSVC non è un'implementazione C pienamente conforme.

Un'altra cosa da notare: se sostituisco a = b con la conversione esplicita a = (void **) b, nessuno dei compilatori genera un avviso. Ho pensato che questo dovesse essere un cast non valido, quindi perché non ci dovrebbero essere avvisi?

Le conversioni del puntatore tramite un cast sono consentite in alcune situazioni. Lo standard C dice quanto segue nella sezione 6.3.2.3p7:

Un puntatore a un tipo di oggetto può essere convertito in un puntatore a un diverso tipo di oggetto. Se il puntatore risultante non è allineato correttamente per il tipo di riferimento, il comportamento non è definito. Altrimenti, quando riconvertito di nuovo, il risultato deve essere paragonato al puntatore originale. Quando un puntatore a un oggetto viene convertito in un puntatore a un tipo di carattere, il risultato punta al byte indirizzato più basso dell'oggetto. Incrementi successivi del risultato, fino alla dimensione dell'oggetto, generano puntatori ai byte rimanenti dell'oggetto.

Quindi puoi convertire tra tipi di puntatore purché non ci siano problemi di allineamento e riconvertire solo (a meno che la destinazione non sia a char *).

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

...

Questo è solo un lavoro sciatto da parte di NVIDIA o, di nuovo, mi manca qualcosa?

Presumibilmente, questa funzione dereferenzia il puntatore dato e scrive l'indirizzo di una memoria allocata. Ciò significherebbe che sta cercando di scrivere a a float *come se fosse a void *. Questo non è lo stesso della conversione tipica in / da a void *. A rigor di termini questo sembra un comportamento indefinito, anche se "funziona" perché i moderni processori x86 (quando non sono in modalità reale) usano la stessa rappresentazione per tutti i tipi di puntatore.


@dbush Molto informativo, grazie! Capisco perché funziona se funziona. Tuttavia, la maggior parte dei compilatori non lancerebbe un avvertimento o addirittura un errore perché &d_Anon ha il tipo richiesto?
CaptainProton42,

3
Fai attenzione a come lo interpreti, ci possono essere e ci sono trucchi tra i template se compili CUDA con un compilatore C ++
talonmies
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.