c language · advanced

C: Classic Undefined Behavior

Undefined behavior (UB) in C means the standard imposes no requirements whatsoever — the program may crash, produce garbage, or appear to work — and, crucially, the optimizer is permitted to assume UB never occurs and transform code accordingly, which is why its effects can be bizarre and non-local. It must be distinguished from implementation-defined behavior (a documented choice, like sizeof(int)) and unspecified behavior (a valid but undocumented choice, like function-argument evaluation order). A core source of UB is sequencing: modifying an object more than once, or reading and modifying it, with no sequence point between, as in a[i] = i++; or printf("%d %d", i++, i++); — the correct answer to 'what does this print' is 'undefined', not a number. The standard catalog every C programmer should recognize includes signed overflow, out-of-bounds array and buffer access (e.g. strcpy into a too-small buffer), use-after-free and double free, dereferencing NULL or an uninitialized/wild pointer, data races, shifting by at least the type width or a negative amount, strict-aliasing violations (type-punning by casting pointers instead of using memcpy or a union), and modifying a string literal. Catch these with -fsanitize=address,undefined, Valgrind, and -Wall -Wextra rather than trusting that 'it worked on my machine'.

🔑 Key line

C undefined behavior imposes NO requirements and the optimizer assumes it never happens: classics are unsequenced read+write (a[i]=i++), signed overflow, OOB/buffer overflow, use-after-free/double-free, null/wild deref, bad shifts and strict-aliasing — catch with ASan/UBSan + Valgrind.

The code

int i = 0;
a[i] = i++; // UB: i read & written with no sequence point
printf("%d %d", i++, i++); // UB: arg eval order unspecified + i changed
int x = INT_MAX + 1; // UB: signed overflow
int* p; *p = 5; // UB: uninitialized / wild pointer
free(q); free(q); // UB: double free
char buf[4]; strcpy(buf, "hello"); // UB: buffer overflow

What this lesson walks through

  1. 01Sequence points & unsequenced access
  2. 02UB is not 'implementation-defined'
  3. 03Gotcha — the optimizer weaponizes UB
  4. 04The usual suspects & how to catch them

Modifying an object more than once, or modifying and reading it, between sequence points is undefined behavior. a[i] = i++; and printf("%d %d", i++, i++); are the textbook examples: the order of the read and the increment (or of the two arguments) is not sequenced, so the result is not just unspecified — it is UB. Don't try to 'reason out' the answer; the correct answer is 'undefined'.

See it animated — step by step, at your own pace

Unlock the full interactive walkthrough of C: Classic Undefined Behavior and 100+ animated C++ interview lessons.

← Previous
C: Integer Promotion & Signed/Unsigned
Next →
C: Struct Padding & Alignment