Ch5.13: Strict Aliasing Rule
Overview
The strict aliasing rule is one of the most subtle and dangerous parts of C++. It allows compilers to assume that pointers of different, unrelated types never refer to the same memory.
In practice, the strict aliasing rule is often ignored by programmers, misunderstood, or violated accidentally.
Violating the rule results in undefined behavior that can silently produce incorrect results even when the code “looks correct”.
In this chapter, you will learn:
- what aliasing means
- what the strict aliasing rule says
- why the rule is often ignored in real-world code
- when aliasing is allowed
- when aliasing is forbidden
- why pointer-based type punning is dangerous
- how to safely inspect raw bytes
- how to safely reinterpret bits using
::std::bit_cast - how to use
[[__gnu__::__may_alias__]]as a last-resort escape hatch - where to learn more (Timur Doumler’s CppCon 2019 talk)
1. What is aliasing?
Two pointers alias when they refer to the same object.
int x{42};
int *p = ::std::addressof(x);
int *q = ::std::addressof(x); // p and q alias the same int
If one modifies the object, the other sees the change. Aliasing becomes dangerous when the pointers have different types.
2. What the strict aliasing rule says
The compiler may assume that pointers to unrelated types do not alias the same object.
“Unrelated types” means types that are not:
- the same type (ignoring
const/volatile) - qualified versions of each other
- members of the same struct/class hierarchy
-
char,unsigned char, orstd::bytemay be used to inspect the raw bytes of any object -
Important:
signed char,uint8_t,uint_least8_t, andchar8_tcannot be used for inspecting raw bytes. They are distinct types and do not have the special aliasing exemption.
If you access an object through a pointer of an unrelated type, the compiler is allowed to assume that this access never happens → undefined behavior.
3. Why the strict aliasing rule is often ignored
Many programmers come from languages where type punning is harmless, or they
learned C from old books that used reinterpret_cast-style tricks.
As a result, real-world codebases often contain:
- pointer casts between unrelated types
- type punning through unions
- reinterpretation of memory through incorrect pointer types
These patterns may “seem to work” on some compilers or architectures, but they are still undefined behavior and can break under optimization.
The fact that strict aliasing violations often appear to work does not make them safe.
4. Allowed aliasing
Same type (ignoring const)
int x{42};
int *p = ::std::addressof(x);
int const *q = ::std::addressof(x); // ok
char / unsigned char / std::byte views
int x{0x12345678};
unsigned char *bytes =
reinterpret_cast(::std::addressof(x));
Reading these bytes is always allowed.
5. Forbidden aliasing (undefined behavior)
Accessing an object through a pointer of an unrelated type
float f{1.0f};
int *p = reinterpret_cast<int*>(::std::addressof(f));
// *p = 0; // ❌ undefined behavior
Type punning through reinterpret_casted pointers
int x{42};
float *pf = reinterpret_cast<float*>(::std::addressof(x));
// float y = *pf; // ❌ undefined behavior
6. Why strict aliasing exists
Strict aliasing enables aggressive compiler optimizations.
If the compiler can assume that pointers of unrelated types do not alias, it can:
- keep values in registers longer
- reorder loads and stores
- eliminate redundant memory accesses
Violating the rule breaks these assumptions and leads to unpredictable behavior.
7. Safe ways to inspect raw memory
These patterns are always safe:
- access an object only through pointers of its own type (or cv‑qualified variants)
- use
char,unsigned char, orstd::byteto inspect raw bytes - avoid pointer-based type punning
8. Safe type punning with ::std::bit_cast
In Ch2.13: Casting, we introduced
::std::bit_cast as the modern, safe way to reinterpret the raw bits
of an object. This is the correct replacement for dangerous pointer-based type
punning.
If you need to reinterpret the bits of a value as another type,
use ::std::bit_cast. Do not use pointer casts.
#include <bit>
float f{1.0f};
// Safe: reinterpret the bits of float as std::uint32_t
std::uint32_t u = ::std::bit_cast<std::uint32_t>(f);
Incorrect (undefined behavior)
float f{1.0f};
std::uint32_t *p = reinterpret_cast<std::uint32_t*>(::std::addressof(f));
// std::uint32_t u = *p; // ❌ undefined behavior
Correct
float f{1.0f};
std::uint32_t u = ::std::bit_cast<std::uint32_t>(f); // ✔ safe
9. As a last resort: [[__gnu__::__may_alias__]] (GCC extension)
If you truly must alias through pointers, GCC provides a non‑standard escape
hatch: [[__gnu__::__may_alias__]].
This is not standard C++. It is non-portable and should only be used by experts who fully understand strict aliasing.
// Detect whether [[__gnu__::__may_alias__]] is supported (GCC and Clang)
#if __has_cpp_attribute(__gnu__::__may_alias__)
# define MAY_ALIAS [[__gnu__::__may_alias__]]
#else
# define MAY_ALIAS
#endif
// Create an alias type for "int*" that is allowed to alias anything
using may_alias_int_ptr = int * MAY_ALIAS;
float f{1.0f};
// Allowed by GCC and Clang (non-portable)
may_alias_int_ptr p = reinterpret_cast(::std::addressof(f));
// GCC/Clang will not assume this violates strict aliasing
int bits = *p;
This attribute disables strict aliasing for that type only.
Prefer ::std::bit_cast whenever possible.
10. Recommended talk: Type punning in modern C++
For a deep, modern explanation of strict aliasing and type punning, watch this CppCon 2019 talk by Timur Doumler:
“Type punning in modern C++ – Timur Doumler – CppCon 2019”
You do not need to understand every detail yet. For now, focus on the core message: type punning through pointers is dangerous, and strict aliasing is the reason why.
Key takeaways
- The strict aliasing rule is subtle and often ignored in real-world code.
- Accessing an object through a pointer of an unrelated type is undefined behavior.
char,unsigned char, andstd::bytemay safely inspect raw bytes;signed char,uint8_t,uint_least8_t, andchar8_tmay not.- Pointer-based type punning is almost always undefined behavior.
::std::bit_castis the modern, safe way to reinterpret bits.[[__gnu__::__may_alias__]]is a GCC/Clang-only escape hatch; it is non‑portable and should not be relied on.- Strict aliasing enables aggressive compiler optimizations.
- Watch Timur Doumler’s CppCon 2019 talk for deeper understanding.