Skip to content
  • Undefined Behaviour Catalogue · C

Undefined Behaviour Catalogue

LearnViaNotes @learnvianotes· 3 min read

Undefined behaviour (UB) means the standard places no requirements on what happens. The program may crash, print garbage, work by luck — or be “optimised” in surprising ways because the compiler assumes UB never happens.

KindMeaningExample
UndefinedAnything can happen; the whole program is invalidSigned overflow, out-of-bounds access, NULL dereference
UnspecifiedOne of several valid outcomes, not documentedOrder of evaluating function arguments
Implementation-definedDocumented choice of the compiler/platformsizeof(int), signedness of char, right shift of negatives
Locale-specificDepends on the current localeisalpha for non-ASCII bytes

The catalogue

Undefined behaviourExample
Signed integer overflowINT_MAX + 1
Division or modulo by zero; INT_MIN / -1x / 0
Shift by negative or ≥ width; left-shift of negative1 << 32, -1 << 1
Array access out of bounds (even reading)a[5] for int a[5]
Pointer arithmetic beyond one-past-the-endp + n + 1
Dereferencing NULL, dangling or misaligned pointers*(int *)NULL
Reading an uninitialised automatic variableint x; printf("%d", x);
Modifying a variable twice between sequence pointsi = i++;
Modifying a string literal or a const objectchar *s = "hi"; s[0] = 'H';
Strict-aliasing violationReading a float through an int *
Use after free, double freesee Memory Bugs
Falling off a non-void function whose value is usedmissing return
Mismatched printf format and argumentprintf("%d", 3.0)
Converting an out-of-range float to an integer(int)1e20
Data race between threadstwo threads write one variable without sync

Signed overflow

C
#include <stdio.h>
#include <limits.h>

int main(void) {
    int x = INT_MAX;
    x = x + 1;                      /* UB — not guaranteed to wrap to INT_MIN */
    printf("%d\n", x);
    return 0;
}
UBSan
main.c:6:11: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'

Because overflow “cannot happen”, an optimiser may turn if (x + 1 > x) into if (1) and for (int i = 0; i <= n; i++) into a loop that assumes n < INT_MAX. Check before the operation:

C
#include <stdio.h>
#include <limits.h>
#include <stdbool.h>

bool safe_add(int a, int b, int *out) {
    if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) return false;
    *out = a + b;
    return true;
}

int main(void) {
    int r;
    printf("%d\n", safe_add(INT_MAX, 1, &r));
    if (safe_add(40, 2, &r)) printf("%d\n", r);
    printf("%d\n", __builtin_add_overflow(INT_MAX, 1, &r));   /* GCC/Clang builtin (C23: ckd_add) */
    return 0;
}
Output
0
42
1

Division by zero

C
#include <stdio.h>

int divide(int a, int b) { return a / b; }

int main(void) {
    printf("%d\n", divide(10, 0));
    return 0;
}
UBSan
main.c:3:37: runtime error: division by zero

Strict aliasing & type punning

The compiler assumes pointers to different types don’t point to the same memory (except char *). To reinterpret bytes, use memcpy — it compiles to a single move:

C
#include <stdio.h>
#include <string.h>
#include <stdint.h>

int main(void) {
    float f = 1.0f;
    uint32_t bits;
    memcpy(&bits, &f, sizeof bits);           /* well-defined type punning */
    printf("0x%08X\n", (unsigned)bits);       /* IEEE 754: sign 0, exponent 127, mantissa 0 */
    return 0;
}
Output
0x3F800000

Living with UB

  • Compile with -Wall -Wextra and fix every warning.

  • Test with -fsanitize=undefined,address (and thread for concurrency).

  • Use unsigned types for bit manipulation and wrap-around arithmetic.

  • Validate inputs before arithmetic, indexing and shifting.

  • -fwrapv makes signed overflow wrap (GCC/Clang) — a band-aid, not a fix.

What is undefined behaviour and why does C have it?

Why can undefined behaviour 'time travel'?

Is i = i++ undefined?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report