Undefined Behaviour (also known as UB) occurs when you violate certain language rules. These rules include (but are not limited to): dereferencing a null pointer, signed integer overflow, accessing through a dangling reference, or accessing through an unaligned pointer.

When undefined behaviour occurs, the C and C++ standards do not place any restrictions on what your program might do. In other words, your program may crash, or continue execution, or call some seemingly unrelated piece of code, or print 42 and open xkcd in your web browser.

In practice, you may be able to reason about how your compiler will respond to UB, and in some cases compilers will guarantee that certain operations are well-defined, but for maximum portability you should aim to keep your programs UB-free.

Examples of UB

int x;
std::cout << x; // UB: x was used before being initialized

int *y = nullptr;
*y = 5; // UB: dereferencing null pointer

int arrayA[10];
for(int z : arrayA)
    std::cout << z << ' '; // UB: array elements used when uninitialized

int *w = new int;
delete w;
*w = 5; // UB: object used after being destroyed

Avoiding UB

Undefined behaviour can be difficult to diagnose. Always compile with warnings enabled, but note that compilers can’t detect all problems at compile time. Tools like ASan, UBSan and Valgrind perform checks on your code at runtime and are good at catching invalid memory accesses, so are recommended for use during development.

For gcc and clang users, we recommend using

-Wall -Wextra -g -fsanitize=address,undefined

in your compilation flags during development. This enables compiler warnings, ASan, UBSan (and debugging symbols for nicer sanitizer error messages).

See Also