Type punning with void * without breaking the stri

2020-02-02 11:13发布

问题:

I recently came across the strict aliasing rule, but I'm having trouble understanding how to use void * to perform type punning without breaking the rule.

I know this breaks the rule:

int x = 0xDEADBEEF;

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

int z = x;

And I know that I can safely use a union in C99 for type-punning:

union{
    int x;
    short y;
} data;

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

int z = data.x;

But how do I use void * to safely perform type-punning in C99? Is the following correct:

int x = 0xDEADBEEF;

void * helper = (void *)&x;

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

int z = x;

I suspect that code will still break the strict aliasing rule since the memory at variable x's address can be modified by both x and a dereferenced y.

If type-punning is undefined via void *, what is the purpose of the void * in C99?

回答1:

void * has nothing to do with type-punning. Its main purposes are:

  1. To allow for generic allocation and freeing operations that don't care about the type of the object the caller is storing there (e.g. malloc and free).

  2. To allow a caller to pass a pointer to an arbitrary type through a function that will pass it back via a callback, (e.g. qsort and pthread_create). In this case, the compiler cannot enforce type checking; it's your responsibility when writing the caller and callback to ensure that the callback accesses the object with the correct type.

Pointers to void are also used in a few places (like memcpy) that actually operate on an object as the overlaid unsigned char [] representation for the object. This could be seen as type-punning, but it's not an aliasing violation because char types are allowed to alias anything to access its representation. In this case, unsigned char * would also work, but void * has the advantage that pointers automatically convert to void *.

In your example, since the original type is int and not a union, there is no legal way to type-pun and access it as short. You could instead copy the value of x to a union, perform well-defined type-punning there, then copy it back. A good compiler should omit the copy entirely. Alternatively, you could break the write down into char writes and then it would be legal aliasing.