What "undefined" actually means
The C standard describes a legal C program. For certain operations it says: "if a program does this, the behavior is undefined." Not "it crashes." Not "it returns garbage." Undefined. The standard has nothing to say. The compiler is free to assume the operation never happens — and to optimize your code under that assumption.
This sounds abstract until you see what happens in practice. A program that triggers UB might crash. It might produce wrong output. It might work perfectly in debug builds and fail in release builds. It might behave differently on different CPUs, compilers, or optimization levels. There is no predictable outcome.
Signed integer overflow
Signed integer overflow is undefined behavior. The compiler assumes it cannot happen and optimizes accordingly. This produces some of the most surprising bugs in C:
Null pointer dereference
Out-of-bounds array access
-fsanitize=undefined,address during development.
UBSan catches signed overflow, null dereferences, out-of-bounds, and more at runtime.
AddressSanitizer catches heap/stack buffer overflows and use-after-free.
These are indispensable tools.
The common sources of UB
- Signed integer overflow
- Null or dangling pointer dereference
- Out-of-bounds array access
- Reading uninitialized memory
- Modifying a string literal
- Calling
free()twice on the same pointer - Data races (multiple threads accessing shared data without synchronization)
- Shift by negative amount or by more than the type's width
Undefined behavior means the compiler assumes it never happens — and rewrites your code around that assumption.