A production bug that made me care about undefined behavior
Nature of the bug: uninitialized vs “true” UB
- Many commenters say this is fundamentally an “uninitialized variable / garbage value” bug, not the more exotic “nasal demons” kind of undefined behavior.
- Others point out that in standard C/C++, reading uninitialized data is UB, and that the “could be anything” outcome is a direct consequence of that.
- Several stress that even if the standard had defined “indeterminate but stable garbage,” the logical bug (assuming a default value) would still exist.
Default initialization and language design
- Strong support for “initialize everything explicitly,” especially for fundamental types.
- Several argue modern languages should (and mostly do) zero‑initialize by default, with an explicit “uninitialized” escape hatch for performance‑critical cases.
- Counterpoint: zero‑init as default can be wrong if all‑zero is not a valid value; some prefer languages that force explicit initialization or a
Default/MaybeUninit-style mechanism (as in Rust). - Some wish C++ had inverted defaults: everything initialized unless explicitly marked
no_init/uninitialized.
C++26 and “erroneous behavior”
- One thread explains that C++26 will treat reading uninitialized variables as “erroneous behavior” rather than UB: compilers are encouraged to diagnose and may assign arbitrary but well‑specified “some value.”
- There is debate over whether this meaningfully restricts optimizations or just formalizes current practice; some find the distinction from UB unclear and possibly toothless.
Compiler optimizations under UB
- Multiple godbolt examples show surprising codegen:
- Partially initialized structs being returned as if both branches executed.
- Functions effectively deleting or skipping code after a UB point.
- Values acting “paradoxically” (different effective values at different uses).
- Some defend this as legitimate: if you leave a value uninitialized, you said “any value is fine,” so the optimizer can pick whatever is convenient.
- Others argue this is “technically correct but practically harmful” and that compilers should treat such values as opaque, not fold them into constants.
Practical advice and structural issues
- Common recommendations:
- Always give struct fields explicit defaults.
- Use sanitizers / runtime checks (including stack poisoning options) to catch uninitialized reads.
- Avoid patterns where structs flip between POD and non‑POD, which can silently change initialization rules.
- Some note the logical design is also flawed: two booleans
success/errorencode impossible combinations; a single status or richer enum / timestamp would be more robust.