2013-04-01 10 views
17

Niedawno natknąłem się na regułę ścisłego aliasingu, ale mam problem ze zrozumieniem, jak używać typu void * do wykonywania typowania bez naruszania reguły.Wpisz wykrawanie z pustką * bez łamania zasady ścisłego aliasingu w C99

Wiem, że to łamie zasadę:

int x = 0xDEADBEEF; 

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

int z = x; 

I wiem, że mogę bezpiecznie korzystać z unii w C99 dla typu paronomazja:

union{ 
    int x; 
    short y; 
} data; 

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

int z = data.x; 

Ale jak mogę używać void * bezpiecznie wykonać typowanie w C99? Jest następujący poprawne:

int x = 0xDEADBEEF; 

void * helper = (void *)&x; 

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

int z = x; 

Podejrzewam, że kod będzie nadal złamać ścisłe reguły aliasingu, ponieważ w pamięci pod adresem zmienne x „s mogą być modyfikowane zarówno x i dereferencjonowane y.

Jeśli cienie są niezdefiniowane przez void *, jaki jest cel funkcji void * w C99?

Odpowiedz

19

void * nie ma nic wspólnego z typowaniem. Jego główne cele to:

  1. Aby umożliwić generycznego przydziału i operacje, które nie dbają o rodzaju obiektu rozmówca jest przechowywanie tam (np malloc i free) zwalniając.

  2. Aby umożliwić wywołującemu przekazanie wskaźnika do dowolnego typu za pośrednictwem funkcji, która przekaże go za pośrednictwem wywołania zwrotnego (np. qsort i pthread_create). W tym przypadku kompilator nie może wymuszać sprawdzenia typu; to jest twoja odpowiedzialność podczas pisania wywołującego i wywołania zwrotnego, aby upewnić się, że wywołanie zwrotne uzyskuje dostęp do obiektu o poprawnym typie.

Wskaźniki do void wykorzystywane są również w kilku miejscach (jak memcpy), które faktycznie działają na obiekcie jako reprezentacja pokrył unsigned char [] dla obiektu. Może to być postrzegane jako wykrywanie typu, ale nie jest to naruszenie aliasingu, ponieważ typy char mogą aliasować wszystko, aby uzyskać dostęp do jego reprezentacji. W tym przypadku również zadziała unsigned char *, ale void * ma tę zaletę, że wskaźniki automatycznie konwertują się na void *.

W twoim przykładzie, ponieważ oryginalny typ to int, a nie związek, nie ma legalnego sposobu na kalamburowanie i dostęp do niego jako short. Zamiast tego możesz skopiować wartość x do związku, wykonać dobrze zdefiniowane tam uderzenie, a następnie skopiować. Dobry kompilator powinien całkowicie pominąć kopię. Ewentualnie możesz zepsuć zapis na char, a następnie będzie to legalne aliasing.

+5

Och, widzę, więc w przypadkach, w których użyty zostanie 'void *', programista powinien już znać prawidłowy typ do obsadzenia; nie stosuje się żadnego typowania, ponieważ używany jest oryginalny typ danych (tzn. 'int *' -> 'void *' -> 'int *'). –

+1

Tak, dokładnie. +1 –

+1

Mogę się mylić, ale czy to nie jest tylko "char", który może pseudonimować, a nie 'unsigned char'? – Arnout

Powiązane problemy