Visual walkthrough — Undefined behavior — comprehensive list, why to avoid
We will not assume you know what a compiler, an overflow, or a "sequence point" is. Every word gets a picture.
Step 1 — What a compiler actually is (the contract picture)
WHAT. A compiler is a translator. You hand it C source text; it hands back machine instructions (the actual 1s and 0s the CPU runs). Think of it as a very literal-minded factory worker.
WHY start here. You cannot understand how code gets deleted until you see that the compiler is allowed to change what you wrote, as long as the visible result matches what the C standard promises. The standard is the contract. The compiler only owes you the outputs the contract lists.
PICTURE. On the left, your source. In the middle, the contract (the C standard) filtering what the compiler must preserve. On the right, the machine code — which may be shorter than your source if the contract lets it skip work.

Step 2 — The number line every int lives on
WHAT. A signed int is a whole number stored in a fixed number of bits — on most machines 32 bits. That means it can only hold values in a finite range. We call the biggest one INT_MAX.
WHY. Our whole example is about what happens when a number tries to grow past the edge of that range. So we must first see the edge.
PICTURE. A number line that does not go on forever. It stops at INT_MAX on the right (and INT_MIN on the left). There is a wall at each end.

See Integer Types and Overflow for where these limits come from.
Step 3 — What "overflow" means, and the fork in the road
WHAT. Overflow is when a calculation produces a result that would land outside the walls of Step 2. INT_MAX + 100 wants to be a number bigger than the biggest int.
WHY this is the crux. Here the C contract makes a fateful split:
- For unsigned integers, overflow is defined — it wraps around like a clock (mod ). Perfectly legal.
- For signed integers, overflow is undefined — the contract says nothing at all.
PICTURE. Two number lines. The unsigned one bends into a circle (a clock) — go past the top and you loop to the bottom. The signed one just... falls off a cliff into a fog of "???".

Step 4 — The key assumption: "UB never happens"
WHAT. This is the single sentence that powers the whole trick. Because signed overflow is undefined, the compiler is entitled to assume your program never triggers it. Not "handles it gracefully" — assumes it is impossible.
WHY the compiler is allowed to. Re-read Step 1: the compiler only owes correct output for programs that obey the contract. A program that overflows a signed int has already left the contract, so the compiler owes it nothing — and may treat that path as dead code that can never execute.
PICTURE. The compiler wearing blinders: every branch that would require signed overflow is greyed out as "unreachable." It literally cannot see those paths as real.

Step 5 — Apply the promise to x + 100 < x
WHAT. Now take the parent note's actual check. We ask: given the promise "no overflow," is x + 100 < x ever true?
WHY. We want to see the compiler's algebra, because its next move depends entirely on the answer.
Reason inside the defined world (where no overflow occurs). Then ordinary school algebra applies: subtract from both sides.
PICTURE. The condition box collapsing: two inputs (x+100 and x) feeding a comparison that the compiler rewrites into a single stamped tile reading FALSE.

Step 6 — Deleting the branch (the vanishing check)
WHAT. An if (false) return -1; can never run its body. So the compiler removes the whole if and its return -1;. Your overflow check is gone from the machine code.
WHY this is legal, not a bug. By the as-if rule (Step 1), deleting code that provably never executes changes no observable behaviour for a contract-obeying program. The compiler is correct. You broke the contract by relying on overflow.
PICTURE. Before/after of the machine code. The red "safety check" block is crossed out; the arrow flows straight to return x + 100.

Step 7 — The fix: reason in the defined domain
WHAT. Rewrite the check so the addition never happens unless it is safe. Compare before adding.
WHY it works. INT_MAX - 100 is always in range (subtracting from the max can't overflow). So the comparison uses only legal arithmetic — no promise is broken, nothing is "always false," and the compiler must keep the branch.
PICTURE. The number line from Step 2 with a marker at INT_MAX - 100. Any x to the right of the marker is flagged before we ever try to cross the wall.

Step 8 — The degenerate & neighbour cases (nothing left uncovered)
WHAT / WHY. The same "assume UB is impossible" engine drives every category from the parent note. Each degenerate input has the same shape: a value that leaves the contract, and a compiler that pretends it can't.
PICTURE. A dashboard of edge cases, each showing the input and the compiler's silent assumption.

| Degenerate input | Why it's UB | What the compiler assumes |
|---|---|---|
x / 0, x % 0 |
division by zero has no value | this divisor is never zero → may delete zero-checks |
INT_MIN / -1 |
result exceeds INT_MAX |
signed overflow → never happens |
x << 32 (32-bit) |
shift count width is UB | count is always in |
*p with p == NULL |
null deref is UB | p is assumed non-null → a later if (p) check may be deleted |
i = i++; |
two writes, no [[Sequence Points and Operator Precedence | sequence point]] |
The one-picture summary

The full chain compressed: your promise → the compiler's assumption → constant-folding → branch deletion → runtime garbage — with the fix branch that never enters the pipeline.
Recall Feynman retelling — say it to a 12-year-old
Imagine you hire a super-fast worker (the compiler) and give him a rulebook (the C standard). The rulebook says: "Some things are so forbidden that if a customer ever asks for them, you may assume no customer ever will." Signed-number overflow is one of those forbidden things.
You write: "IF adding 100 makes my number go smaller, sound the alarm." But going smaller can only happen by overflowing — a forbidden thing. So the worker reasons: "Adding 100 making it smaller is forbidden, so it never happens, so the alarm never rings, so I'll rip the alarm out to save time." He hands you a machine with no alarm.
Then a real customer overflows the number. It quietly turns into garbage, and the alarm you carefully installed is gone — the worker removed it before the store even opened. The fix: never phrase your alarm in terms of the forbidden thing. Instead of "did it overflow?" ask "is it about to?" — check x > INT_MAX - 100 first, using only legal math. That alarm the worker is forced to keep.
Recall Quick self-test
Why is if (x + 100 < x) deleted but if (x > INT_MAX - 100) kept? ::: The first relies on signed overflow (UB) so the compiler proves it's always false and removes it; the second uses only in-range arithmetic, so it's a real, non-constant branch.
Can a UB on a later line change an earlier line? ::: Yes — the compiler propagates the "this never happens" assumption backwards, e.g. deleting an earlier if (p) because a later *p implies p is non-null.
Does signed overflow wrap? ::: On hardware often yes, but the compiler is not required to honour that — it's UB, so it may optimize as if it never occurs.
Related build-up: Pointers and Memory in C · malloc and free — Dynamic Memory · Strict Aliasing Rule.