Compilers & Code Generation

the strict-aliasing rule

Two pointers 'alias' when they might point to the same memory. If the compiler cannot rule that out, it must be pessimistic: after a write through one pointer, it cannot trust any value it had cached from the other, so it reloads and recomputes. The strict-aliasing rule is a promise the C and C++ standards make TO the compiler that lets it rule out aliasing in many cases — and it is one of the most surprising rules to systems programmers.

The rule says, roughly: you may access an object in memory only through an lvalue of a compatible type (the object's own type, or a few blessed exceptions, notably char, which may alias anything). The practical consequence: the compiler is allowed to ASSUME that two pointers of unrelated types — say an int* and a float* — do NOT point to the same object. This is type-based alias analysis (TBAA). Because of that assumption, after a store through a float* the compiler may keep using a value it loaded earlier through an int*, without reloading, because the standard promises the two cannot overlap. That assumption enables aggressive caching of values in registers, hoisting loads out of loops, and reordering memory operations. But if you actually DID make an int* and a float* point at the same bytes and wrote through one expecting to read the change through the other, you broke the promise — that is undefined behavior, and the optimizer's assumption can produce results that look impossible.

It matters because this is a classic, real-world source of 'it worked at -O0, broke at -O2' bugs, especially the type-punning idiom of casting a pointer to reinterpret bytes (treating a float's bits as an int by casting float* to int* and dereferencing). That cast-and-deref is exactly the strict-aliasing violation. The honest guidance: the legal way to reinterpret bytes is memcpy() (which the compiler optimizes well) or, in C, a union; char* is also exempt and may legally alias any type. The escape hatch is -fno-strict-aliasing, which tells the compiler to abandon the assumption and treat memory more conservatively (at some performance cost) — useful for legacy code that relies on type punning, but the better fix is to stop aliasing across types.

// UB: type punning via cast violates strict aliasing float f = 1.0f; unsigned bits = *(unsigned *)&f; // reading a float's bytes through unsigned* -> UB // CORRECT, well-defined way to reinterpret the bytes: unsigned bits; memcpy(&bits, &f, sizeof bits); // the compiler optimizes this to the same code, legally

Casting float* to unsigned* and dereferencing is the strict-aliasing violation; memcpy() is the legal, equally fast way.

char* (and unsigned char*) may legally alias any type, and a union or memcpy() is the defined way to reinterpret bytes; casting between unrelated pointer types and dereferencing is undefined behavior — -fno-strict-aliasing only masks the symptom.

Also called
strict aliasingtype-based alias analysisTBAA型別別名分析