violating strict aliasing
Suppose you and a friend each keep your own ledger, and you both promise never to scribble in each other's. With that promise, neither of you needs to re-read the other's ledger after every change — you can trust your own copy. C's optimizer makes a similar bet about memory: it assumes that two pointers of DIFFERENT, incompatible types do not refer to the same bytes. That assumption is the strict aliasing rule, and quietly breaking it is a subtle but very real form of undefined behavior.
More precisely, the rule says: you may access the bytes of an object only through a pointer of the object's own type (or a few permitted exceptions, notably a char pointer, which is allowed to alias anything). If you store a float and then read those same bytes through an unrelated int pointer, you have violated strict aliasing, and the result is undefined behavior even though, naively, the bytes are 'just sitting there'. The optimizer relies on this to skip redundant memory reloads: if it has a value cached in a register and you write through a differently-typed pointer, it assumes that write could not have touched your value, so it keeps using the stale register copy. The classic broken idiom is type-punning: writing 'int bits = *(int*)&my_float;' to inspect a float's bit pattern. The honest, well-defined ways to reinterpret bytes are to copy them with memcpy (the compiler optimizes this away) or to use a union — both sidestep the rule.
Why this matters: strict-aliasing bugs are among the hardest to diagnose because the code looks reasonable, compiles cleanly, and often works at -O0, only to misbehave at -O2 when the optimizer actually exploits the assumption. The practical rules of thumb: do not reinterpret memory by casting between unrelated pointer types; use memcpy or a union to read an object's bytes as another type; reach for char* (or unsigned char*) when you genuinely need to walk raw bytes; and if you must disable the rule for legacy code, the gcc/clang flag -fno-strict-aliasing exists, but relying on it is a crutch, not a fix.
/* BROKEN: read a float's bits through an int pointer (strict-aliasing UB) */ float f = 1.0f; int bad = *(int *)&f; /* CORRECT: copy the bytes, no aliasing violation */ int good; memcpy(&good, &f, sizeof good); /* compiler optimizes this to a move */
Casting between unrelated pointer types to reinterpret bytes is UB. memcpy (or a union) does it with defined behavior and no runtime cost.
A char* (or unsigned char*) is allowed to alias any object — that exception is exactly why memcpy works. But casting between, say, int* and float* to share storage is undefined, and the breakage typically appears only at higher optimization levels.