This is the crux of the problem. When I put on my security hat, I absolutely agree with you: we should give safety rails to programmers, because decades of experience have shown that programmers are incapable of writing correct code without them.
But when you go around and start talking to most C programmers, they don't want those safety rails, they instead want maximum performance. I won't name and shame anyone specific here, but comments along the lines of "we write in C because it keeps all the idiot programmers out of our project" are not unknown. (And then some of those people turn around and complain when the compiler didn't compile their broken code in the way they expected it to, ugh.)
To me, the biggest problem with C isn't that it has undefined behavior, nor that it lacks the safety rails. It's that it gives you undefined behavior while simultaneously avoiding giving you any tools to enforce avoidance of it (and in a couple of cases, it downright encourages you to commit it. Try checking for signed integer overflow without triggering it!). Contrast this to Rust. Rust doesn't have any less undefined behavior--it actually has even more cases that are undefined--but at the same time, it makes it harder to actually go trip over those cases. In the function pointer example, your basic function pointer is required to be non-null, and it's annoying to construct a null function pointer, so you instead it wrap it in Option. But once it's in Option, there's no (easy) way to call it without first making sure that it's non-null, so you end up with code that can never actually call a null function pointer.
But when you go around and start talking to most C programmers, they don't want those safety rails, they instead want maximum performance. I won't name and shame anyone specific here, but comments along the lines of "we write in C because it keeps all the idiot programmers out of our project" are not unknown. (And then some of those people turn around and complain when the compiler didn't compile their broken code in the way they expected it to, ugh.)
To me, the biggest problem with C isn't that it has undefined behavior, nor that it lacks the safety rails. It's that it gives you undefined behavior while simultaneously avoiding giving you any tools to enforce avoidance of it (and in a couple of cases, it downright encourages you to commit it. Try checking for signed integer overflow without triggering it!). Contrast this to Rust. Rust doesn't have any less undefined behavior--it actually has even more cases that are undefined--but at the same time, it makes it harder to actually go trip over those cases. In the function pointer example, your basic function pointer is required to be non-null, and it's annoying to construct a null function pointer, so you instead it wrap it in Option. But once it's in Option, there's no (easy) way to call it without first making sure that it's non-null, so you end up with code that can never actually call a null function pointer.