cpp core · advanced

Strict Aliasing & Type Punning

Strict aliasing lets the compiler assume that pointers to unrelated types never refer to the same object, which enables it to keep values in registers and reorder loads and stores. Reading a float's storage through a uint32_t* therefore breaks the rule and is undefined behavior: it may appear to work in a debug build yet read a stale value, garbage, or be optimized away at -O2. The correct way to reinterpret an object's bytes is to copy them into a brand-new object of the target type — with std::memcpy (which compilers fold to a single move) or, in C++20, std::bit_cast, which is constexpr and self-documenting. Union type punning is defined in C but technically UB in C++, so prefer bit_cast/memcpy for portability. The exception to strict aliasing is char/unsigned char/std::byte pointers, which may alias any object.

🔑 Key line

Reading an object through a pointer of an incompatible type violates strict aliasing and is undefined behavior — the optimizer assumes differently-typed pointers never alias — so pun bits safely by copying into a new object with std::memcpy or std::bit_cast (C++20), never via reinterpret_cast.

The code

float f = 1.0f; // storage bytes: 3F 80 00 00
// (1) UB: type punning through a pointer
uint32_t bad = *reinterpret_cast<uint32_t*>(&f); // strict-aliasing violation
// (2) OK: copy the bytes into a real uint32_t
uint32_t ok1;
std::memcpy(&ok1, &f, sizeof f);
// (3) OK, C++20: same bytes, brand-new object, constexpr
auto ok2 = std::bit_cast<uint32_t>(f);
// union punning: fine in C, technically UB in C++
union {
float f;
uint32_t u;
} pun; // read pun.u after writing pun.f

What this lesson walks through

  1. 01The goal — and the tempting trap
  2. 02Strict aliasing: distinct types can't alias
  3. 03Why it actually bites at -O2
  4. 04Fix #1: std::memcpy into a real object
  5. 05Fix #2: std::bit_cast (C++20)
  6. 06Union punning: a C habit, not C++
  7. 07Pun bits by copying, never by casting

We want the raw bit pattern of f (1.0f -> 0x3F800000). The tempting move is to cast the float's address to a uint32_t* and dereference it. It even prints the right number... which is exactly why the bug survives.

See it animated — step by step, at your own pace

Unlock the full interactive walkthrough of Strict Aliasing & Type Punning and 100+ animated C++ interview lessons.

← Previous
Static Initialization Order Fiasco
Next →
C Fundamentals: Struct Padding, Union, Bit Fields, alignas, Pointer Arithmetic