Falsehoods programmers believe about null pointers

Null pointers, offsets, and array indexing

  • Several comments expand on the idea that a crash address near zero (e.g., 0x420) often indicates a null base pointer plus a field offset in a struct or class; the offset can help locate the offending field.
  • There is debate over whether array indexing on a null pointer is meaningfully distinct: in C, p[n] is *(p + n), so doing pointer arithmetic on a null pointer is already undefined behavior, even if the computed address is non-zero.
  • Some low-level patterns (like unions of pointer-or-error codes, or large structs/arrays starting at address 0) can yield non-obvious offsets that complicate diagnosing null-related crashes.

Undefined behavior and compiler behavior

  • Multiple participants emphasize that dereferencing a null pointer is undefined behavior in C/C++, and that trying to be “clever” about what actually happens is dangerous.
  • Historically, compilers mostly treated UB as “will probably just access address 0”, but modern optimizers aggressively exploit UB, leading to reordering, dead-code elimination, or “time travel”-like effects.
  • There’s disagreement on how much programmers should care about the standard: some prioritize strict conformance to keep future compilers predictable; others focus on testing against specific compilers/platforms and writing straightforward code.
  • Discussion dives into whether “undefined” means “defined somewhere else”; several commenters push back strongly, clarifying that UB is intentionally unspecified and may vary across compilers, versions, flags, and hardware.

C as a “high-level” language and alternatives

  • Some argue C is more of a high-level “abstract machine” language than people admit, making it ill-suited as a simple “portable assembler.”
  • Others note C++ is slowly fixing longstanding portability issues (e.g., mandating two’s-complement integers, proposed fixed 8-bit bytes) but becomes too complex for small systems.
  • There’s a lively Rust/Zig vs C thread: some see Rust/Zig as serious modern replacements; others argue they’re unavailable or impractical on many targets, or unsuitable for certain low-level tasks (e.g., complex garbage collectors).

Signals/exceptions vs explicit null checks

  • The article’s suggestion of “ask for forgiveness” (rely on faults/signals instead of null checks) is widely criticized for C/C++:
    • SIGSEGV cannot reliably distinguish null dereferences from other memory faults.
    • It depends on UB (null address unmapped), may create fragile signal handlers, and can interact badly with optimizations.
  • Some note that, in managed languages with JITs, omitting explicit null checks and relying on hardware traps can be a real optimization on hot paths, but the thrown exceptions themselves are very slow.
  • Others argue a single null check is trivial for CPUs and easily predicted, so the practical win is dubious without hard data.

Embedded, OS behavior, and hardware details

  • Several comments highlight that address 0 may be valid on some embedded platforms (RAM, flash, vector tables), so using NULL as “always invalid” is dangerous there unless the MPU is configured to trap it.
  • There’s clarification that in the C standard, a null pointer is guaranteed not to compare equal to any valid object/function pointer, even on systems where address 0 is usable memory.
  • Claims that Windows “always” turns null dereferences into access violations are refuted with references to counterexamples and subtle behaviors.

Usefulness and genre of the article

  • Some readers like the historical and low-level detail; others find it trivia-heavy and misleading for average programmers, who should simply treat null dereference as UB and never rely on observed behavior.
  • There’s criticism that the piece misuses the “falsehoods programmers believe” genre, which traditionally targets real-world assumptions that break user-facing software (e.g., names, time zones), rather than niche language-lawyer corner cases.