NULL pointers are a red-herring. If we're talking about C level languages, then a NULL pointer dereference is a *good* kind of mistake - it's a clear error and causes an immediate exception at runtime, as you say. Much, much more insidious in C-level-akin languages is stale pointers and code continuing to twiddle memory that no longer references the state it thinks it should. Give me an immediate segfault over bugs which only cause crashes later, in unrelated code, any day! Further, even in managed-runtimes and with non-nullable types, you *still* can have stale references that cause bugs (which can still be hard to debug, even if it manages to better protect state from being corrupted).
The problem is just so much more fundamental: The correct management of the lifetime of state, and the co-ordination of visibility of that state.
(Functional programmers would of course argue the problem is best solved by never sharing state, only ever copying and transforming it. Course, while that removes scope for many kinds of crash-causing bugs, it still can't remove higher-level, logical mistakes..).