So if the choice is between C and Go, for userspace code, Go is the answer.
As proven by UNIX father's design of Inferno and Limbo, where C is only allowed in the kernel and DisVM implementation.
Go is an improved C. It's for people who like C but need C's well-understood problems fixed.
C-style pointers are not a problem. They don't need to be fixed. They're great, and that's why Go retains them.
Here is an example of C-style code making essential use of C-style pointers (especially nil pointers) but it's Go:
https://bugfix-66.com/50788214f539d50382528e86242eb3c846b03f...
You need to be able to represent addresses, and you need to be able to say "address of nothing" (i.e., nil pointer).
Once you've seen Sum Types it's obvious that you just wanted Option<T> - a sum of None and Some of your pointer / reference / whatever type T.
Thus type T must actually point at / be something, whereas Option<T> can be None, and your APIs can be design accordingly e.g. the function which always adds a Dog to this Kennel should take a Dog, not an Option<Dog> that would be silly, but the function to take the most recent addition to the Kennel back out would return Option<Dog> not Dog because if the kennel is empty there is no latest addition to return.
Said in another way... if it makes any sense (this is at the nexus of philosophy and arithmetic):
Since anything resembling emptiness or length of 0 requires a reference then 0 comes after the one (1, monad).
1 is primordial over 0.
Existence wraps emptiness.
For existence to wrap not emptiness nor 0 it must wrap nil. However no matter how you reference nil you must do so through at least one layer of indirection.
In Rust for example Option<&T> is literally the same size as &T because the &T is implemented as a non-NULL pointer, so the None value fits in a niche where NULL would go if this was a pointer (this is called the Guaranteed Niche Optimization, many other more elaborate niche optimizations are routinely performed by the compiler but are not promised, however Rust promises the optimization we need here will always happen)
The machine code ends up identical to what you'd get from unsafe languages with raw pointers if you remembered all the necessary checks, however the safe source code written by humans has stronger typing preventing them from writing code that would e.g. put a NULL Dog in a Kennel, an easy mistake in the unsafe languages.
Some languages solve just this one narrow problem by having specifically "Nullable" and "Non-nullable" types, plus some way to get the Non-nullable from a Nullable (with a branch if it's null) essentially Option but built-in to the language. But wait, as well as this Option feature you also want Result which is another Sum type. Should you add a special built-in for that too? Some languages choose to do so. OK, and how about ControlFlow? Poll? There will be others. I believe that languages should just suck it up and offer Sum types.
It is actually the other way around, having a way to represent “no pointer” is fine. What’s missing is the ability to check at compile time that a pointer is not nil.
The key is that once a value has definitely been obtained, the compiler needs to be able to track that, otherwise it is unclear whether the value needs to be checked again whenever it is used. Often those checks are not where they should be, leading to many bugs.