Strict Aliasing in C++: What It Is and How Not to Shoot Yourself in the Foot

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

What is the strict aliasing rule and why might your C++ code suddenly break?

\n

Have you ever written seemingly perfectly correct C++ code, only to have the compiler at high optimization levels (-O2 or -O3) turn it into something non-functional? Welcome to the world of strict aliasing (strict aliasing rule). This is one of the most insidious rules of the language, capable of turning debugging into a real quest. At PythonLib, we love explaining complex topics in simple terms, and today's article is no exception.

\n

Let's imagine: you have two pointers of different types pointing to the same memory location. The compiler, following the strict aliasing rule, has every right to decide that they cannot refer to the same object. And if you violate this rule — expect undefined behavior (UB). The program might work, it might crash, or it might produce incorrect results. Sounds scary? Let's figure it out.

\n\n

The essence of the rule: why is the compiler so "strict"?

\n

The strict aliasing rule in C++ (and C) states: accessing an object through a pointer or reference of an incompatible type is UB. Exceptions exist only for char, unsigned char, and std::byte. Why is this needed? The answer is simple: optimization. If the compiler is certain that two pointers of different types cannot point to the same memory, it can safely reorder operations, cache values in registers, and aggressively optimize code. Without this rule, the performance of many programs would drop significantly.

\n

But there is a downside: developers who try to perform type punning (interpreting the same bytes as different types) often step on a rake. A classic example is trying to quickly check if a floating-point number is NaN via a reinterpret_cast to int. The compiler can "break" such code.

\n\n

What does a strict aliasing violation look like? Code examples

\n

Let's look at the most popular example, which travels from one Stack Overflow to another:

\n
#include <iostream>\n#include <cstdint>\n\nint main() {\n    float f = 3.14f;\n    // Strict aliasing violation: reading a float as an int\n    int* pi = reinterpret_cast<int*>(&f);\n    std::cout << *pi; // UB!\n    return 0;\n}\n
\n

At first glance — a common trick for viewing the bit representation of a float. But the C++ standard says: float and int are incompatible types. The compiler can optimize this code so that you get garbage. With optimizations enabled, GCC, for example, might simply return some predetermined value or even remove the code entirely.

\n

Another example — working with byte arrays:

\n
struct Point { int x, y; };\nchar buffer[sizeof(Point)];\n// Fill buffer...\nPoint* p = reinterpret_cast<Point*>(buffer);\np->x = 42; // UB, if buffer was not created as a Point\n
\n

Here, the violation occurs because buffer is a char array, and we are accessing it as a Point. Although char* can point to any object, the reverse is not true: Point* cannot point to char[] unless the memory was allocated as a Point.

\n\n

Comparison of approaches: how to safely circumvent strict aliasing?

\n

There are several ways to legally perform type punning in C++. We will compare three main approaches: memcpy, union, and std::bit_cast (C++20). Each has its pros and cons.

\n\n

1. memcpy — the good old way

\n

The safest and most portable method is to copy bytes via memcpy. Compilers optimize this call excellently, often turning it into a simple register load, so there is no overhead:

\n
#inc                

Blogs

Book Recommendations