2013-04-01 9 views
17

Recentemente mi sono imbattuto nella rigida regola di aliasing, ma ho difficoltà a capire come usare void * per eseguire punizioni di tipo senza infrangere la regola.Digitare punire con void * senza infrangere la rigida regola di aliasing in C99

So che questo rompe la regola:

int x = 0xDEADBEEF; 

short *y = (short *)&x; 
*y = 42; 

int z = x; 

E so che posso utilizzare in modo sicuro un sindacato in C99 per il tipo-giochi di parole:

union{ 
    int x; 
    short y; 
} data; 

data.x = 0xDEADBEEF; 
data.y = 42; 

int z = data.x; 

Ma come faccio a usare void * in modo sicuro eseguire la punzonatura di tipo in C99? È la seguente corretta:

int x = 0xDEADBEEF; 

void * helper = (void *)&x; 

short *y = (short *)helper; 
*y = 42; 

int z = x; 

ho il sospetto che il codice sarà ancora rompere la rigida regola aliasing in quanto la memoria all'indirizzo x variabili 's può essere modificata sia da x e Dereferenced y.

Se il tipo di punzonatura non è definito tramite void *, qual è lo scopo dello void * in C99?

risposta

19

void * non ha nulla a che fare con il tipo di punteggiatura. I suoi scopi principali sono:

  1. per permettere l'allocazione generica e operazioni che non si preoccupano tipo dell'oggetto chiamante sta memorizzando là (per esempio malloc e free) liberando.

  2. Per consentire a un chiamante di passare un puntatore a un tipo arbitrario tramite una funzione che lo restituirà tramite una richiamata (ad esempio qsort e pthread_create). In questo caso, il compilatore non può imporre il controllo del tipo; è la vostra responsabilità durante la scrittura del chiamante e del callback per garantire che la richiamata acceda l'oggetto con il tipo corretto.

puntatori a void sono utilizzati anche in alcuni punti (come memcpy) che in realtà operano su un oggetto come rappresentazione sovrapposta unsigned char [] per l'oggetto. Questo potrebbe essere visto come tipo puning, ma non è una violazione di aliasing perché i tipi char sono autorizzati ad aliasare qualsiasi cosa per accedere alla sua rappresentazione. In questo caso, anche lo unsigned char * funzionerebbe, ma void * ha il vantaggio che i puntatori vengono convertiti automaticamente in void *.

Nell'esempio, dal momento che il tipo originale è int e non è un unione, non esiste un modo legale per digitare e accedere come short. Potresti invece copiare il valore di x in un'unione, eseguire punizioni di tipo ben definite lì, quindi copiarlo. Un buon compilatore dovrebbe omettere completamente la copia. In alternativa, è possibile suddividere l'annotazione nelle scritture char e quindi sarebbe un alias legale.

+5

Oh capisco, quindi nei casi in cui verrebbe utilizzato 'void *', il programmatore dovrebbe già conoscere il tipo corretto da trasmettere; non viene eseguito alcun tipo di punteggiatura dal momento che viene utilizzato il tipo di dati originale (ad esempio 'int *' -> 'void *' -> 'int *'). –

+1

Sì, esattamente. +1 –

+1

Potrei sbagliarmi, ma non è solo 'char' che è autorizzato ad aliasare qualcosa, non' char unsigned'? – Arnout

Problemi correlati