ISO C became unusable for operating systems development
I think undefined behavior (as a general concept) gets an unfair share of the blame here. It&#x27;s notable that almost all criticism of undefined behavior in C tends to focus on just two sources of UB: signed integer overflow and strict aliasing; other sources of UB just don&#x27;t generate anywhere near the same vitriol [1]. Furthermore, it&#x27;s notable that people <i>don&#x27;t</i> complain about UB in Rust... which arguably has a worse issue with UB in that a) there&#x27;s not even a proper documentation of what <i>is</i> UB in Rust, and b) the requirement that &amp;mut x be the sole reference to x (and therefore is UB if it is not) is far more stringent than anything in C (bar maybe restrict), and I&#x27;m sure that most Rust programmers, especially newbies starting out with unsafe, don&#x27;t realize that that&#x27;s actually a requirement.<p>There is a <i>necessity</i> for some form of UB in a C-like language, and that has to deal with pointer provenance. You see, in C, everything lives in memory, but on real hardware, you want as much to live in a register as possible. So a compiler <i>needs</i> to be able to have reasonable guarantees that, say, any value whose address is never taken can never be accessed with a pointer, and so can be promoted to a register. As a corollary, this requires that things like out-of-bound memory accesses, or worse, converting integers to pointers (implicating pointer provenance here) need to have UB in at least some cases, since these could in principle &quot;accidentally&quot; compute an address which is the same as a memory location whose address was never taken.<p>That suggests that the problem isn&#x27;t UB per se. If we look at the two canonical examples, arithmetic overflow and strict aliasing, we can see that one of the features of these things is that they have a pretty obvious well-defined semantics [2] that can be given for them, and furthermore, there&#x27;s no way to <i>access</i> these well-defined semantics even avoiding this feature altogether. And I think it&#x27;s the lack of this ability to work around UB that is the real issue with C, not UB itself.<p>[1] For example, it is UB to pass in rand as the comparison function to qsort. I&#x27;m sure many people will not realize that before I wrote this, and even parsing the C specification to find out that this is UB is not trivial. For an interesting challenge, try giving a definition of what the behavior should be were it not UB--and no, you can&#x27;t just say it&#x27;s impl-defined, since that still requires you to document what the behavior is.<p>[2] I will point out that, for arithmetic overflow, this semantics is <i>usually wrong</i>. There are very few times where you want &lt;large positive number&gt; + &lt;large positive number&gt; = &lt;negative number&gt;, and so you&#x27;re mostly just swapping out an unpredictably wrong program for a predictably wrong program, which isn&#x27;t really any better. However, the most common time you do want the wrapping semantics is when you want to <i>check</i> if the overflow happened, and this is where C&#x27;s lack of any overflow-checked arithmetic option is really, really painful.