int a[3]; a[5] = 1; // out-of-bounds: undefined behavior
C++
Topics in this category
Casting & Conversions
Compilation & Linking
Concepts & Constraints
Concurrency
Const Correctness
Constexpr & Compile-Time
Coroutines
Enums & Enum Classes
Exception Handling
I/O Streams
Lambda Expressions
Memory Management
Modern C++
Move Semantics
Namespaces
OOP & Classes
Operator Overloading
Preprocessor & Macros
RAII & Smart Pointers
Ranges & Views
C++
Undefined Behavior
Undefined behavior is program behavior the C++ standard places no requirements on—anything can happen: a crash, wrong results, or seemingly working code that breaks later or under optimization. The compiler may assume UB never occurs and optimize accordingly. UB arises from invalid operations like out-of-bounds access, use-after-free, or signed overflow.
Real-world example
Code that 'worked' in debug crashes in release because the optimizer exploited an assumption of no UB.
Memory Management
Undefined Behavior
Undefined Behavior
What is the difference between undefined, unspecified, and implementation-defined behavior?
BeginnerUndefined behavior has no constraints—anything may happen (must be avoided). Unspecified behavior has several allowed outcomes and the implementation need not document which (e.g., order of evaluation of function arguments). Implementation-defined behavior is unspecified but the implementation must document its choice (e.g., sizeof(int)). Only UB is dangerous; the others are just non-portable in defined ways.
Real-world example
sizeof(long) is implementation-defined, argument evaluation order is unspecified, and a[5] on a size-3 array is UB.
Compilation & Linking
Undefined Behavior
Undefined Behavior
Common UB: out-of-bounds array/container access, dereferencing null or dangling pointers, use-after-free/double-free, reading uninitialized variables, signed integer overflow, data races (unsynchronized concurrent access), violating strict aliasing, invalid downcasts with static_cast, and returning references to locals. Many are memory- or lifetime-related.
int x; std::cout << x; // reading uninitialized value: UB
Real-world example
A data race between two threads on the same variable is UB and produces intermittent, hard-to-reproduce bugs.
Memory Management
Concurrency
Undefined Behavior
Unsigned overflow is defined to wrap modulo 2^n. Signed overflow is undefined because the standard doesn't fix a representation-independent result, and leaving it UB lets compilers optimize (e.g., assume x+1 > x). This means relying on signed wraparound is unsafe; use unsigned types or explicit checks when wraparound semantics are needed.
int x = INT_MAX; x + 1; // signed overflow: UB
unsigned u = UINT_MAX; u + 1; // defined: wraps to 0
Real-world example
An overflow check written as 'if (x + 1 < x)' is optimized away because signed overflow is UB.
Undefined Behavior
Casting & Conversions
Undefined Behavior
Strict aliasing says you generally may not access an object through a pointer/reference of an incompatible type (with some exceptions like char/std::byte). Violating it—e.g., reinterpreting a float* as an int* and reading through it—is UB, and compilers optimize assuming differently-typed pointers don't alias. Use std::bit_cast, memcpy, or unions carefully to reinterpret bytes safely.
float f=1.0f;
int i = *reinterpret_cast<int*>(&f); // strict-aliasing UB
Real-world example
A hand-rolled float-to-int bit hack breaks under optimization due to strict aliasing; std::bit_cast fixes it.
Casting & Conversions
Undefined Behavior
Undefined Behavior
An uninitialized local variable holds an indeterminate value; reading it is UB because its bits are arbitrary and, for some types, may not even be a valid value. The program may appear to work by luck, then fail unpredictably. Always initialize variables (e.g., int x = 0; or brace-init) before use.
int x;
if (x > 0) { /* UB: x is uninitialized */ }
Real-world example
An uninitialized flag intermittently enables a code path, causing a bug that only appears on some runs.
Undefined Behavior
Memory Management
Undefined Behavior
Because compilers assume UB never happens, they may delete checks or code paths that would only matter if UB occurred—e.g., removing a null check after a dereference (which implied the pointer was non-null), or assuming a loop terminates. This can make bugs appear far from their cause and behave differently across optimization levels, making UB especially insidious.
int* p = get(); *p; if (p) use(p); // compiler may drop the null check
Real-world example
The optimizer removes a redundant-looking null check because an earlier dereference implied non-null, causing a crash on null input.
Undefined Behavior
Memory Management
Undefined Behavior
Prevent UB with safe practices: initialize variables, use containers/at()/bounds checks, smart pointers and RAII, avoid manual casts, and enable warnings (-Wall -Wextra). Detect it with sanitizers (UndefinedBehaviorSanitizer, AddressSanitizer, ThreadSanitizer), static analysis (clang-tidy, cppcheck), and thorough tests run under these tools in CI.
g++ -fsanitize=undefined,address -g prog.cpp
Real-world example
Running tests under UBSan and ASan in CI catches out-of-bounds and overflow bugs before release.
Memory Management
Undefined Behavior
Undefined Behavior
Dereferencing a null pointer is undefined behavior. In practice it often causes a segmentation fault/crash, but the standard doesn't guarantee that—optimizers may assume the pointer is non-null and behave unexpectedly. Always check pointers (or use references/smart pointers/std::optional) before dereferencing when they might be null.
int* p = nullptr; int x = *p; // UB (often a crash)
Real-world example
A missing null check on a lookup result crashes the app when the key is absent.
References & Pointers
Undefined Behavior
Undefined Behavior
A data race occurs when two or more threads access the same memory location concurrently, at least one access is a write, and there's no synchronization ordering them. The C++ memory model declares data races undefined behavior because the result depends on unpredictable timing and CPU/compiler reordering. Prevent them with mutexes, atomics, or by not sharing mutable state.
// two threads doing counter++ without a mutex/atomic: data race (UB)
Real-world example
An unsynchronized shared counter produces wrong totals and intermittent crashes—classic data-race UB.
Concurrency
Undefined Behavior
Undefined Behavior