4 ms·
A comment on your blog post. You are writing: > In C89, undefined behavior is interpreted as, “The C standard doesn’t have requirements for the behavior, so yo
by lqet 6y ago
A comment on your blog post. You are writing:
> In C89, undefined behavior is interpreted as, “The C standard doesn’t have requirements for the behavior, so you must define what the behavior is in your implementation, and there are a few permissible options”.
But isn't the sentence
> Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results
in the C89 spec already giving the compiler developers carte blanche to do whatever they want? Anything can be interpreted as "ignoring the situation with unpredictable results".
In your criticism of C99 (where the word "permissible" was changed to "possible") you write:
> In C99 undefined behavior is interpreted as, “The C standard doesn’t have requirements for the behavior, so you can do what ever you want”. [..]. If for instance you have a large codebase like say the Linux Kernel and there is a single instance of undefined behavior somewhere in there the compiler is free to produce a binary that does what ever it wants. It doesn’t have to document what it does, it doesn’t have tell the user, it doesn’t need to do anything.
This exactly fits the C89 spec. The compiler may just ignore that you are relying on undefined behavior and produce a completely unpredictable binary.
- quelsolaar 6y agoIf the change makes no difference at all, why do you think the change was made? Changes in specs are made because people lobby for them.
- klodolph 6y agoI think the relevant phrase is "so you must define what the behavior is in your implementation". With simple compilers, you could more or less define what happens with undefined behavior. For example, on platform X, if you dereference NULL, you get a segfault. On platform Y, you get a zero value. Problem is these behaviors are hard to define once you start thinking about the edge cases. You can dereference NULL with an offset, and once you do that, you might skip over any guard pages and overwrite a function pointer with garbage and then jump to some random location in memory, and once you do that, all bets are off. I mean, really, really off. So that leaves you with three options. 1. Abandon the idea of defining behavior in these cases. 2. Insert bounds checks. 3. Refine the idea of undefined behavior into two separate concepts--"bounded" and "critical" undefined behavior. C has taken all three options at the same time, in a sense. The main standard takes option #1, the Address Sanitizer gives you #2 (more or less), and annex L gives you #3. Annex L, option #3 above, is in many senses the sane option--but it requires a fairly large amount of work on the part of the compiler writers, and to my knowledge neither GCC nor Clang have implemented it. I would also like to add that some of the places where we see undefined behavior were inserted into the standard for real-world cases for unusual platforms. For example, the weird rules for pointer comparisons make sense when you think about segmented memory. Relational comparisons (<, >, <=, >=, but NOT == or !=) on pointers are generally used for traversing arrays, and if you have two pointers into the same array, it would be reasonable to assume that they have the same segment (depending on your memory model)... SO, you omit the segment comparison in this case.
- quelsolaar 6y agoI think there are valid reasons for a language spec to have Undefined behavior. But many UB in C would be much better if the where defined as platform specific. Like integer overflow, its very well defined on x86, ARM and every other CPU architecture in use, but not only does compilers act as if its unknowable, even when they clearly know it because they are implementing a known instruction set, they even make it hard to detect overflows since they optimize away tests since they cant happen.
- klodolph 6y agoI’d say that Annex L does get you closest to that goal, in that “bounded undefined behavior” can result in indeterminate values. > …not only does compilers act as if its unknowable… I disagree with this interpretation. The compiler is acting not as if it’s unknowable, but as if it doesn’t happen.
- MauranKilom 6y agoSurely you understand why compilers do this, right? This isn't compilers and standard writers being mean because they hate you. This is a language that you choose for speed being optimal for speed. In particular, signed integer overflow being UB was and still is a necessary evil so that compilers don't have to add ugly loop preambles to every loop you write.