23

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?

Vilhelm Gray
  • 11,516
  • 10
  • 61
  • 114

1 Answers1

23

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.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 7
    Oh I see, so in the cases that `void *` would be used, the programmer should already know correct type to cast; no type punning occurs since the original data type is used (i.e. `int *` -> `void *` -> `int *`). – Vilhelm Gray Apr 01 '13 at 14:24
  • 1
    I may be wrong, but isn't it only `char` that is allowed to alias anything, not `unsigned char`? – Arnout Dec 02 '13 at 14:04
  • 5
    @Arnout: No, it's any character type (there are three: `char`, `signed char`, and `unsigned char`). The representation is defined in terms of `unsigned char`, however, and in fact `unsigned char` is the one you want to use for accessing it portably since signed types have representation uniqueness and/or completeness issues unless the signed representation is twos complement. – R.. GitHub STOP HELPING ICE Dec 03 '13 at 00:27
  • 1
    @R.. You say that "you can do type-punning in union". But I found in C99 spec: 6.2.6.1 General "7. When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values.". In other words if you store union.short = value; and then read it as union.int the .int part of union is unspecified and far from "well defined type punning" right? Or did I miss something? – Vit Bernatik Apr 26 '17 at 17:07
  • @VitBernatik: The language you cite exists because there are some platforms where larger stores are sometimes cheaper than smaller ones. If, for example, an implementation supports 8-bit and 32-bit stores, but not 16-bit stores, and a program includes a union that combines a 16-bit `short` and a 32-bit `float`, processing a writes to the `short` member using a 32-bit store may be more efficient than using two 8-bit stores. The authors of the Standard did not want to forbid such treatment on platforms where it was useful. – supercat Sep 06 '18 at 19:00