2016-06-07 21 views
14

C'è this answer in un'altra domanda sull'utilizzo di cudaMalloc((void**)&device_array, num_bytes), che utilizza void** come argomento di output anziché passare un valore void* come valore di ritorno come lo standard malloc.Cosa c'è di sbagliato nel cast come (void **) e device_array?

Critica API di NVIDIA e afferma:

Casting, come in (void **) & device_array, non è valido C e si traduce in un comportamento indefinito .

ed è stato svitato più volte (8 fino ad ora), quindi presumo ci sia del vero in esso.

Non capisco cosa c'è di sbagliato nel casting lì.

  • Che cos'è C non valido?
  • In tal caso ciò comporterebbe un comportamento indefinito?

Tutto quello che so è che si compila senza preavviso e corre con il comportamento previsto per me. Ma non sono a conoscenza di C fino al livello di specifiche standard.

+0

Stessi problemi se si esegue il comando 'int *' per 'float *'. 'void **' non è speciale allo stesso modo 'void *' is. – immibis

+0

Quindi, non è corretto scrivere questo? 'int val = 0x4229B26C; \t printf ("il float rappresentato in memoria da 0x% X ha un valore decimale di% .03f \ n", val, * ((float *) &val)); ' – bct

+0

Dai un'occhiata a [questo post SO] (http://stackoverflow.com/questions/15818906/does-this-pointer-casting-break-strict-aliasing-rule) – LPs

risposta

8

Il problema è che void* ha un significato speciale in C, con regole speciali (1). È l'unico tipo di puntatore a/da cui è possibile convertire in modo sicuro qualsiasi altro tipo di puntatore. Tuttavia, queste regole speciali non si applicano in modo ricorsivo a void**.

Il che significa che il codice come int* ptr = malloc(x); è perfettamente bene, ma

int* ptr; 
cudaMalloc(&ptr, x); // bad 

non va bene! Una conversione puntatore da int** a void** non è ben definita. In teoria ciò potrebbe causare un comportamento non definito e un disallineamento (2).

Inoltre, potrebbero esserci anche problemi con l'alias del puntatore. Il compilatore è libero di presupporre che il contenuto di un void** non sia mai accessibile tramite un int** e potrebbe quindi ottimizzare il codice in modi imprevisti, portando a un comportamento indefinito per violazione della regola di aliasing (6.5).

Il che significa che si dovrà scrivere codice come questo al fine di utilizzare in modo sicuro la funzione:

void* vptr; 
int* iptr; 

cudaMalloc(&vptr, x); 
iptr = vptr; 

(1) C11 6.3.2.3/1:

Un puntatore per annullare può essere convertito in o da un puntatore a qualsiasi tipo di oggetto . Un puntatore a qualsiasi tipo di oggetto può essere convertito in un puntatore a vuoto e viceversa; il risultato deve essere uguale al puntatore originale .

(2) C11 6.3.2.3/7:

Un puntatore a un tipo di oggetto può essere convertito in un puntatore a un tipo di oggetto diverso . Se il puntatore risultante non è allineato correttamente per il tipo di riferimento, , il comportamento non è definito.

+0

Non vedo davvero dove l'API sia all'origine di una cosa orribile. Sembra che tutte le funzioni che prendi il device_array prendi '' void * '', quindi non c'è bisogno di usare '' int * '' ovunque.Probabilmente il codice di esempio dell'altra domanda è l'unica cosa orribile. – atturri

+0

@atturri 'void *' non è un tipo significativo per conto proprio. Da qualche parte nel programma, ci sarà una conversione al tipo previsto. Ma se questa conversione è sempre nascosta dietro un livello di astrazione, l'API potrebbe andare bene. Non so davvero nulla di Cuda, quindi non posso dirlo. Toglierò il mio commento a riguardo, poiché è soggettivo senza alcun contesto dato. – Lundin

+0

@Lundin Bella risposta. A proposito del commento di atturi, nel caso di allocazione di memoria di cuda void * non dovrebbe mai essere convertito in qualcos'altro, almeno sul codice host. In realtà punta a un indirizzo nella memoria grafica che non è accessibile dalla CPU ... Quindi, dopo averci pensato, direi che il problema è solo che il tipo di array_dispositivo deve essere vuoto * e non int *? Quindi tutto andrebbe bene? .... – bct

Problemi correlati