But nowadays UB is something much more complex and dangerous than "I can't ever know the results of this".
If C could have a consistent set of rules, and/or easily tag something as undefined a la "unsafe" or have some sort of visual reminder signal (like using function name prefixes or suffixes) that would go a long way to making it better.
The cases of undefined behavior in the C standard are independent of compiler settings or options.
> If C could have a consistent set of rules …
The C language has a well-defined standard, but the presence of undefined behavior is a deliberate aspect of that standard.
You can have well defined standards, that are wildly inconsistent. For example, in python, in file A:
def get(dict, key):
return dict[key]
In file B: def get(key, dict):
return dict[key]
Imagine working in this codebase!I'm reminded of a quote: [0]
> Ada has made you lazy and careless.
> You can write programs in C that are just as safe by the simple application of super-human diligence.
To your point about performance:
> compiler can make choices based on certain assumptions in the name of performance
I don't think the performance argument really applies with modern optimising compilers. The (too often overlooked) Ada language is safer than C, but has about the same performance, provided you avoid the features with runtime overhead. Similarly I don't think the performance of Rust, and in particular its Safe Rust subset, suffers much for its lack of undefined behaviour.
It's true that, say, Java, doesn't perform as well as C even today, but Java requires a slew of runtime checks and is hostile to micro-optimised memory-management. In Ada, things like array bounds checks can be enabled for debug builds but disabled for production builds, which isn't easy to do in C.
> If C could have a consistent set of rules, and/or easily tag something as undefined a la "unsafe" or have some sort of visual reminder signal (like using function name prefixes or suffixes)
This essentially can't be done. Even the MISRA C coding style, which aims to help with this kind of thing, can't completely guarantee to eliminate undefined behaviour from C codebases. To illustrate the challenge of 'containing the risk' with the C language, it's undefined behaviour to do this:
int i; int j = i;
Fortunately other languages do a much better job at offering truly safe subsets (Rust and D for instance).[0] https://people.cs.kuleuven.be/~dirk.craeynest/quotes.html