What You'll Learn
- The 5 most common UB sources
- How compilers exploit UB for speed
- Sanitizers to detect UB automatically
- Safe alternatives for every UB pattern
Understanding Undefined Behavior
Undefined Behavior (UB) is the most dangerous concept in C++. When your code triggers UB, the compiler is allowed to do literally anything — crash, produce wrong results, or appear to work perfectly today and fail in production tomorrow. Understanding UB is what separates safe C++ from ticking time bombs.
The 5 Most Common Sources of UB
Signed integer overflow, out-of-bounds access, null pointer dereference, reading uninitialized memory, and invalid bit shifts account for the vast majority of UB bugs. Each one has a safe alternative that costs almost nothing at runtime.
Common Mistake: Testing code and concluding "it works." UB can produce correct results on your machine, with your compiler, today — and fail catastrophically after a compiler update or on a different platform.
Common UB Sources
See the 5 most common UB traps and their safe alternatives
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
// Undefined Behavior (UB) means the compiler can do ANYTHING
// — crash, produce wrong results, or even appear to "work" today
// and break silently tomorrow.
int main() {
cout << "=== Common UB Examples (safe demos) ===" << endl;
// 1. Signed integer overflow — UB!
// int x = INT_MAX;
// x = x + 1; // UB — compiler assumes this never happens
cout << "INT_MAX = " << INT_MAX << endl;
cout <<
...How the Compiler Exploits UB
The compiler assumes your code never has UB. This is how it optimizes: if dereferencing a pointer is UB when null, the compiler assumes the pointer is never null — and can remove your null checks. This makes UB bugs extremely hard to debug because the "safety net" code you wrote gets silently deleted.
Pro Tip: Compile with -Wall -Wextra -Werror and -fsanitize=undefined in debug builds. UBSan catches most UB at runtime with minimal overhead.
Compiler UB Exploitation
See how the compiler assumes and optimizes around UB
#include <iostream>
#include <cstring>
using namespace std;
// The compiler EXPLOITS UB for optimization
// If your code has UB, the optimizer can remove checks,
// reorder statements, or delete entire branches.
// Example: compiler removes null check because
// dereferencing null is UB, so pointer "can't" be null
void demonstrateCompilerAssumptions() {
cout << "=== Compiler Exploits UB ===" << endl;
// The compiler reasons: if you dereference p,
// then p cannot be null (otherwis
...Safe Alternatives — Modern C++ Patterns
For every UB source, modern C++ provides a safe alternative: std::optional for nullable values, .at() for bounds-checked access, std::span for safe array views, and checked arithmetic for overflow prevention.
Safe Alternatives
Replace every UB pattern with a safe modern C++ version
#include <iostream>
#include <vector>
#include <optional>
#include <string>
#include <span>
#include <numeric>
using namespace std;
// Modern C++ provides safe alternatives to every UB source
// Safe division with optional
optional<double> safeDivide(double a, double b) {
if (b == 0.0) return nullopt;
return a / b;
}
// Bounds-checked array wrapper
template<typename T, size_t N>
class SafeArray {
T data[N]{};
public:
T& at(size_t i) {
if (i >= N) throw out_of_range("In
...Quick Reference
| UB Source | Safe Alternative |
|---|---|
| Signed overflow | Unsigned types or checked arithmetic |
| Out-of-bounds | .at() or std::span |
| Null dereference | std::optional or check first |
| Uninitialized read | Always initialize ( or = 0) |
| Invalid shift | Check shift amount < bit width |
Lesson Complete!
You now understand what undefined behavior is, why it's dangerous, and how to write UB-free C++ code.
Sign up for free to track which lessons you've completed and get learning reminders.