
I used to think that, but I no longer do. What changed my mind is that C23 redefined realloc(ptr, 0) to be UB, with the explicit purpose of allowing downstream standards to refine the contract:
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf
Classifying a call to realloc with a size of 0 as undefined behavior would allow POSIX to define the otherwise undefined behavior however they please.
So, I think, after all UB means what it says on the tin: just a mere lack of any requirements whatsoever. This absence of guarantees can be used for different purposes: for optimization, to make compilation possible at all (it is a fallacy to think that non optimizing compiler somehow dodges UB), or, as in the realloc case, to define the behavior elsewhere.
I wish that everyone adopted “Checked Illegal Behavior” and “Unchecked Illegal Behavior” terminology instead. It just more precisely expresses the phenomenon we care about with respect to memory safety and all that stuff. Certain runtime behavior of the program are “bad” simply because they are deemed to be so by fiat. It’s just the letter of the law, without obligatory connection to hardware.
This legalistic view makes it easy to see the role of compiler as a translator between the high-level language contract and the low-level language contract.
score 1 · canonical
(to clarify, my claim is “can be used for”, not “exists for”, I am agnostic about UB’s original intended purpose)
That’s the issue for C though, everything is observable, and why I think any discussion about optimization in the context of C UB is ultimately a red herring. Consider:
int a = 1; int b = 2; int* a_ptr = &a; int* feeling_lucky_ptr = a_ptr + 1; *feeling_lucky_ptr += 1; printf("%d", b);You just can’t explain what a non optimizing compiler would do to this program, without some notion of (Unchecked) Illegal Behavior.
score 1 · canonical