Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I think the problem is cultural in the C community. C programmers have Stockholm Syndrome around UB optimizations.

As TFA notes, "There is No Reliable Way to Determine if a Large Codebase Contains Undefined Behavior" https://blog.llvm.org/2011/05/what-every-c-programmer-should...

That's because UB is a bug that occurs as your program runs (e.g. dereferencing a null pointer). You'd have to prove that your program is free of bugs to prove that you won't encounter UB at runtime, which is not possible in general.

But C programmers believe they deserve to be abused this way.

"Sure, the C compiler is allowed to nuke your entire program whenever there's a bug at runtime, but all you have to do is prove that your program is bug free! Why, any real C programmer can write bug-free code, so if you have any UB bugs, you're probably not good enough to write C. Sure, I've been bruised by UB bugs in the past, we all have, but when I made those mistakes, I deserved to be bruised, and those bruises made me a better programmer."



I expect there's already been a lot of evaporative cooling in the C community and there will only be more over time. Increasingly the people who are going to be left in the C community are precisely those people who think C is generally OK. Anyone who has developed a high degree of fear of C is ever-increasingly doing what it takes to move away.

I have tried to phrase this neutrally, as to not be a comment about whether or not C "really is" generally OK. I am not sure I have 100% succeeded, as I have my opinions. But the point stands regardless; the people in the C community are generally going to be the ones who are OK with C.


> But C programmers believe they deserve to be abused this way.

I don't know of any C programmers who think that way. We accept the state of affairs because there is little alternative. We're not going to rewrite our code in another language, because we mostly need to write in C for various reasons. Also Rust inherits LLVM's undefined behaviour, so it's no panacea. We mostly keep adding flags to turn off the most excessive optimisations and pray.


Rust inheriting it is the equivalent of a codegen bug, and they get patched. The cause here isn’t LLVM, it’s language semantics.


Anecdotally, I find about as many bugs in my Java code at work, as I do in my hobby C code, including "dereferencing null pointers" aka NullPointerExceptions.


"Undefined behavior" is a term of art relevant to C, meaning that the standard no longer has any comment about what happens. Thus the comments about launching nukes, or destroying your machine, etc., being standards complaint, even though obvious real compilers won't actually emit code that does that.

Dereferencing a nil pointer in Java is not undefined behavior. It is defined; it throws a NullPointerException, and "it throws a NullPointerException" is a very specific term of art in the Java community that is very precisely defined.

Many languages don't have an explicit concept of "undefined behavior" in their standard (though, of course, due to deficiencies in the standard they may have implicitly undefined behavior), and of those that do I doubt anyone matches C or C++ in the commonly-used category of languages.


In single-threaded programs, Java will neatly do what you expected, it lacks this excitement from C, when you try to dereference a null pointer, iterate past the end of an array or whatever in Java you'll just get some sort of Throwable...

But in concurrent programs things get exciting again. Java would like to be somewhat fast on actual hardware, and the actual hardware behaviour of memory is exciting, so, Java is also exciting, depending on how your hardware works.

You won't get C's full-blown "Undefined behaviour", but you can get some pretty astonishing outcomes, for example time travel can happen. If one thread changes A, then B, then C, it is permissible in Java that from the perspective of another concurrent thread the value A is unchanged, after B and/or C have changed.

It turns out humans are bad at coping with this even though it's much less scary than Undefined Behaviour, and so some more modern languages than Java offer ways to get memory behaviour that doesn't hurt your brain as much in exchange for reduced performance.


Point taken about NPEs being defined behavior, but from a practical point of view, a bug is a bug (and bugs is what the parent comment was referring to).

Whether the bug was due to a well-defined or undefined behavior seems like an exercise in assigning blame to another entity (the language, the committee, the compiler, etc).


Correct assignment of (technical) blame is an important engineering task, though. You sound to me like you are thinking that's some sort of wrong thing to do, but I would disagree. Identifying whether the bug is a defined (in C) behavior or the result of the compiler making a certain choice as the result of your code invoking undefined behavior is an important element of fixing the bug; if you don't know which is which you're operating under a critical handicap.


NPE isn't really a relevant comparison point - it raises an exception of sorts in both languages. Accessing an array past its end, accessing freed memory, reading uninitialized memory seem more apt.


These are all issues a compiler could insert checks for, saving you the time to do it manually, while trading performance for security or correctness.

C doesn't trade performance away, so if you'd like to pay the price of these checks, it's on you, the programmer to add them in. C programmers have to put in extra effort to make the executables safer and output correct results.

Other languages trade off performance for safety and/or correctness. The programmers using them have to put in extra effort to make the executables run as fast as they do in C.

Ultimately, programmers tend to make rational choices, and the stockholm syndrome mentioned above is really just C programmers dealing with the consequences of the trade-off they made by using C.


C is designed to allow programmers to insert or remove checks as performance tuning. Compiler UB "optimizations" that remove that ability from the programmer make the language unusable.


Not knowing what is UB in C is equivalent to not knowing a core part of the C language. In that situation, yes, C would seem unusable for people who have superficial knowledge of it.


Nobody knows UB rules. The WG14 discussions are full of confusion.


Send Linux Torvalds a note and tell him he has only superficial knowledge of C.


The situation has gotten at least somewhat better since then. Ubsan and friends are not a guarantee, but make it much more realistic to find UB issues.


Good point. C programmers have come to assume that C is a shitty language with terrible semantics and it's their fault for not using Rust or something. They blame the language for the mess made by standards/compilers.


I don't really like Rust; I like C. But, it could be improved, making one with less undefined behaviour and better macros and less confusing syntax for types etc. Many of the new programming languages that try to avoid the bad thing from C I think are avoiding most of the good thing from C, too.


You don't get it: if you are ready to give up top performance, you can choose among a multitude of more forgiving languages.

There is simply no point in having a C language with less than top level optimizations.


Name a feature of C that makes it inherently "faster" than another compiles-to-machine-code language. I can name plenty that make it slower.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: