C compilers remove your bounds checks and null checks that you, the programmer, explicitly wrote in the program, even if they are known to fail, and then crash on input that your checks would have prevented. The real scary thing is that people had to write code to determine that you're checking array bounds and more code to remove the check.
No, observing a pointer is not the same as dereferencing it.
if(ptr == NULL) puts("Hey, this pointer is NULL!");
does NOT get optimized out, because checking whether a pointer is NULL is NOT undefined behavior, and so the compiler cannot assume that the pointer is never null. Likewise with bounds checking, trying to ACCESS an array beyond its bounds is undefined behavior, but doing a simple arithmetical comparison on the number before using it as an array index is not. As for overflow, just compare to INT_MAX before doing any operation that will increase the value, and use unsigned wherever you can. The one serious flaw with C is that it has the "const" qualifier which really only means readonly; you can't use const objects as the size of static arrays, since the compiler can't guarantee that the object won't be modified via a pointer-to-mutable-object.