/glossary/c/undefined-behavior
What is undefined behavior in C?
- tool
- C
- versions
- C11, C17, C23
- os
- any
Undefined behavior (UB) in C is code for which the language standard places no requirements. When a program executes it, the compiler may produce a crash, a wrong result or a program that seems to work, and it may change between compiler versions or optimization levels. Common causes are signed integer overflow, dereferencing a null or dangling pointer and reading outside an array.
Why it matters
The compiler assumes that your program never triggers UB, and it optimizes under that assumption. If a check can only be true after an overflow, the compiler is allowed to remove the check. A bug can therefore stay hidden at -O0 and show up at -O2, as described on gcc -O2 optimization levels. A crash such as a segmentation fault is one possible result of UB, and it is the lucky one, because it is visible.
Common sources
| Source | Example | Why it is undefined |
|---|---|---|
| Signed integer overflow | INT_MAX + 1 | The result does not fit in `int`; unsigned arithmetic wraps, signed does not |
| Null pointer dereference | *p where p is NULL | No object lives at address zero |
| Out-of-bounds access | a[10] for int a[5] | The index is outside the array object |
| Use after free | Reading memory after free(p) | The object no longer exists |
| Uninitialized read | int x; return x; | An automatic variable has no value until it is set |
| Shift too far | 1 << 32 for a 32-bit int | The shift count is not less than the width of the type |
| Division by zero | x / 0 | The result of the operation is not defined |
Catch it with the sanitizer
GCC and Clang can add run-time checks with -fsanitize=undefined. The next program adds one to INT_MAX when run with no arguments, which is signed overflow.
cat > ub.c <<'CODE'
#include <limits.h>
#include <stdio.h>
int main(int argc, char **argv) {
(void)argv;
int x = INT_MAX;
x = x + argc;
printf("%d\n", x);
return 0;
}
CODE
gcc -fsanitize=undefined -o ub ub.c && ./ubOutput
ub.c:6:7: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int' -2147483648
The sanitizer prints a line of the form file:line:column: runtime error: ... with the operation and the types involved, then lets the program continue unless you add -fno-sanitize-recover. It reports only the code that runs, so it needs tests that reach the faulty path. Warnings from -Wall -Wextra (gcc -Wall -Wextra) catch the cases the compiler can prove at build time.
How to avoid it
- Check a value before an arithmetic operation that could overflow, or use unsigned types where wrapping is intended.
- Initialize every variable and check the result of every allocation, as covered in malloc vs calloc.
- Keep array indices inside the bounds, and pass lengths with pointers.
- Run tests with the sanitizer on in CI, in a separate build from the one you ship.
- Do not rely on "it works on my machine": UB is a property of the code, not of one run.
FAQ
Does undefined behavior always crash the program?
No, and that is the danger. The C standard places no requirement on the result, so the program can crash, print a wrong value, appear to work or behave differently after a compiler update or a change in optimization level. A program that ran correctly in a test can still contain undefined behavior.
How is undefined behavior different from unspecified and implementation-defined behavior?
With unspecified behavior the standard allows several outcomes and the program stays valid, such as the order in which function arguments are evaluated. Implementation-defined behavior must be documented by the compiler, such as the size of int. Undefined behavior has no such limit and applies to the whole program.
How do I find undefined behavior in my C program?
Compile with the undefined behavior sanitizer, gcc -fsanitize=undefined or clang -fsanitize=undefined, and run your tests. It checks at run time and prints the file and line of each violation it executes, such as signed integer overflow. Add -Wall -Wextra for the cases the compiler can see without running the code.