Type punning and Unions in C
Solution 1:
Yes, storing one member of union and reading another is type punning (assuming the types are sufficiently different). Moreover, this is the only kind of universal (any type to any type) type punning that is officially supported by C language. It is supported in a sense that the language promises that in this case the type punning will actually occur, i.e. that a physical attempt to read an object of one type as an object of another type will take place. Among other things it means that writing one member of the union and reading another member implies a data dependency between the write and the read. This, however, still leaves you with the burden of ensuring that the type punning does not produce a trap representation.
When you use casted pointers for type punning (what is usually understood as "classic" type punning), the language explicitly states that in general case the behavior is undefined (aside from reinterpreting object's value as an array of char
s and other restricted cases). Compilers like GCC implement so called "strict aliasing semantics", which basically means that the pointer-based type punning might not work as you expect it to work. For example, the compiler might (and will) ignore the data dependency between type-punned reads and writes and rearrange them arbitrarily, thus completely ruining your intent. This
int i;
float f;
i = 5;
f = *(float *) &i;
can be easily rearranged into actual
f = *(float *) &i;
i = 5;
specifically because a strict-aliased compiler deliberately ignores the possibility of data dependency between the write and the read in the example.
In a modern C compiler, when you really need to perform physical reinterpretation of one objects value as value of another type, you are restricted to either memcpy
-ing bytes from one object to another or to union-based type punning. There are no other ways. Casting pointers is no longer a viable option.
Solution 2:
As long as you only access the member (int
or float
) which was most recently stored, there's no problem and no real implementation dependency. It's perfectly safe and well-defined to store a value in a union member and then read that same member.
(Note that there's no guarantee that int
and float
are the same size, though they are on every system I've seen.)
If you store a value in one member and then read the other, that's type punning. Quoting a footnote in the latest C11 draft:
If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.