breaks strict aliasing, and has undefined behavior. If you compile it with g++ -O2, you may get unexpected results. The solution is to use a union, or to compile with -no-strict-aliasing.
I used to have a lot of code like this, which worked fine on older compilers, but not on newer compilers. It's perfectly reasonable code, so I'm not sure why the newer compilers don't produce the expected behavior by default.
>I'm not sure why the newer compilers don't produce the expected behavior by default.
The reason is that there are optimizations that a compiler can do when it knows that two particular pointers will not reference the same location in memory at a given time (which is called aliasing). These optimizations have to do with memory access. If pointers cannot be proven not to alias each other, then the order in which they are loaded, modified, and stored will be very rigid since it is impossible to tell if changing the order will change the end result. Sometimes that's just life; you might have some complex interactions going with your pointers, and it may actually be that the order of operations can't be changed.
But if you can assume that the pointers don't alias each other, then the compiler is free to change the order of operations for better efficiency. It can often remove unnecessary loads and stores (if x and y don't alias, you don't have to reload y just because you changed x), and it can also put loads together at the beginning and stores together at the end (which is faster because, um, caching? I'm not really clear on that part).
The catch is that in general, there is no way for the compiler to know whether two pointers can alias each other. In C99, you can inform the compiler of this with the restrict keyword, but that requires that you do some careful analysis of your code to make sure you aren't lying to the compiler. If you mess up, you may end up with bugs that are very difficult to track.
So that's where strict aliasing comes in. Usually, two pointers of different types won't point at the same memory location. So they added the "strict aliasing" rule to make that fact official, because it means that in the vast majority of cases, code gets a nice speedup without any changes or difficult reasoning. Using a union is basically a way of informing the compiler that you need it to make an exception this one time.
I used to use a macro something like this to make the process concise:
int i = PUN(int, x); // evil floating point bit level hacking
i = 0x5f3759df - (i >> 1); // what the fuck?
x = PUN(float, i);
x = x*(1.5f-(xhalf*x*x));
Modern CPU's can actually "look ahead" in the execution flow. So for something like this:
MOV EAX,[memory_access]
ADD EBX,ECX
ADD EAX,EBX
The ADD EBX,ECX instruction would be done while the CPU would otherwise be idle, waiting for the memory subsystem to return the result of the first instruction.
Of course there are strange corner cases that have to be taken into account. For example, if the first instruction ends up causing an exception -- maybe the memory it refers to is in the swap file, or maybe it's dereferencing a NULL pointer -- the CPU has to be able to roll back the execution of any subsequent instructions, so it appears to the software as if the exception happened during the instruction that actually caused it.
If you're interested in the automatic program transformations performed by modern CPU's, you can read more by Googling for the Intel processor optimization manual [1] -- warning, PDF link. (I believe AMD publishes a similar document.)
In practice (that I've found, with GCC), the memcpy and single-use temporaries get optimized away entirely. In strict C99, writing one member of a union and then reading a different member of the same union is undefined in the general case, last I checked. http://cellperformance.beyond3d.com/articles/2006/06/underst... seems to agree, but suggests that every major compiler recognizes it as a de facto idiom and supports it anyway.
Type punning through a union is explicitly defined to work in C99 and C11 (footnote 95 in C11):
> 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’’).
Please don't help spread the myth that compiler writers can break this idiom. That said, I use memcpy in my own code.
I don't think you can use unions. In the draft of the ISO-IEC 9899 standard (C99) I have, paragraph 6.2.6.1.7 (I'm not kidding) says
> 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.
So no undefined behaviour (nasal demons and such) but still unspecified.
> x = (float)&i;
breaks strict aliasing, and has undefined behavior. If you compile it with g++ -O2, you may get unexpected results. The solution is to use a union, or to compile with -no-strict-aliasing.
I used to have a lot of code like this, which worked fine on older compilers, but not on newer compilers. It's perfectly reasonable code, so I'm not sure why the newer compilers don't produce the expected behavior by default.