One faulty premise, several different outcomes

A new programming demonstration published on October 25 highlights a familiar but frequently misunderstood risk in C and C++: once a program invokes behavior that the language standard leaves undefined, developers cannot safely reason about the result as though the compiler will execute each source statement literally.

The demonstration, written by programmer Thomas Habets, responds to the idea that undefined behavior is harmless because a faulty operation will merely trigger a segmentation fault. His examples instead show compiler output changing substantially when the source contains an unreachable marker or an invalid pointer operation. In one case, a function that does not appear to be called in the source becomes part of the generated behavior.

Habets then replaces the unreachable marker with code that writes through a null pointer. According to the published experiment, the generated program attempts a write of zero to address zero rather than preserving the value 99 from the source, and then emits an invalid instruction that terminates execution. The constant 99 is absent from that generated program.

A small alteration produces another result. When the null pointer is advanced before the write, the compiler generates behavior closer to a naive line-by-line interpretation. Habets reports the same broad pattern from GCC and Clang. The contrast is the point of the test: superficially similar invalid operations do not create a stable fallback behavior that programmers can depend upon.

Optimizers rely on language guarantees

Compilers optimize on the assumption that a valid program follows the rules of its language. If reaching a particular operation would itself violate those rules, the optimizer is allowed to treat that path as impossible while transforming the surrounding code. It is not required to preserve a programmer’s preferred failure mode, nor must it diagnose every possible instance before producing a binary.

The experiment also cautions against treating a successful test run as proof that invalid code is acceptable. Output can vary with a different compiler, optimization setting, target architecture or nearby source change. A crash observed today does not establish that the same bug will reliably crash tomorrow before damaging state or exposing data.

The practical conclusion is narrow but important: undefined behavior should be removed rather than incorporated into a program’s design. Developers investigating such bugs can use sanitizers, warnings and reduced test cases, but those tools do not convert an invalid operation into a portable contract. Habets’ examples demonstrate why reasoning must begin with valid source code, not with assumptions about what a particular machine usually does when the rules have already been broken.