Why is null-terminated C string considered a "billion dollar mistake", but UB isn't?

The "billion-dollar mistake" was about implicitly nullable values, i.e., allowing a variable with type `T` to also be set to `null`, not null-terminated strings.

Anyway, one argument is that UB is fundamentally useful in languages that are insufficiently type-safe, like C and C++. The "holes" in the specification allow for regions where the compiler can optimize the code in ways you may not expect.

As we have developed more advanced type systems, the utility of undefined behavior has lessened considerably.

Agreed that this is why a lot of people support the current UB situation, but the history of UB makes this feel wrong:

> As far as I can tell, C89 did not use performance as a justification for any of its undefined behaviors. They were non-portabilities, like signed overflow and null pointer dereferences, or they were outright bugs, like use-after-free. But now experts like Chris Lattner and Hans Boehm point to optimization potential, not portability, as justification for undefined behaviors. I conclude that the rationales really have shifted from the mid-1980s to today: an idea that meant to capture non-portability has been preserved for performance, trumping concerns like correctness and debuggability.

https://research.swtch.com/ub

Oh undoubtedly; I didn't mean to imply that performance was the reason behind the origin of UB, though I can see how my comment would read that way. Thank you for adding the note!

[deleted]

Null terminated strings were an intentional compromise, known to be inferior for execution but superior for memory

Null being an "allowed" value for pointers is the mistake e.g. what became nullptr. "Allowed" because garbage values are garbage.

Think of the alternative where we'd be dealing with endless issues because someone though 255 or 2^16-1 characters ought to be enough for everyone.

VB6 :)

This comes from a fundamental misunderstanding of what UB is.

Think of this piece of code - `y * x / y`.

Would you like to simplify it to just `x` ?

You need to either lean on UB to do so or have some magical way to prove that y can not be 0.

Otherwise this transformation changes behavior, and is illegal.

Why not just execute what I wrote? Maybe I'm doing some rounding?

Because we want people to write simple idiomatic code that runs fast today and also in ten years on alien hardware.

> Maybe I'm doing some rounding?

Compilers won't do this optimization when it is illegal to. If you disagree with the compiler's idea of what is legal, you can either write inline assembly, or put this code into an always inlined, but never optimized function.

Because we want compilers to perform the optimizations, not programmers.

Probably because null-terminated strings are completely avoidable, whereas some amount of UB is all but required for performance (albeit C and C++ have far too much).

I recommend this explanation of why UB is good and necessary (but C and C++ are doing it wrong, defining some things as UB that really shouldn't be): https://www.ralfj.de/blog/2021/11/18/ub-good-idea.html