5 ms·
> If the standard says that say, dereferencing a NULL pointer is UB it means you're not supposed to do that ever, on any OS or compiler. Says who? The standar
by Kamq 3y ago
> If the standard says that say, dereferencing a NULL pointer is UB it means you're not supposed to do that ever, on any OS or compiler.
Says who?
The standard doesn't say that. At most it says that if you're trying to write portable C, you shouldn't do that. But not everyone is trying to write something portable.
- dale_glass 3y agoThe standard does say it. UB means there are absolutely no rules regarding what happens. It's not implementation defined, it's "absolutely anything can happen and it can change unpredictably".
- Kamq 3y ago> UB means there are absolutely no rules regarding what happens. I will acknowledge that the standard says this, but the original post said "you're not supposed to do that ever". Those are not the same thing.
- dale_glass 3y agoThey are. First, because the compiler isn't bound to any promise. If in version 2.0, you can still use memory immediately after freeing it, version 2.0.1 is perfectly free to make the program misbehave. No consistency has been promised. Second, because per the linked article, compilers treat UB in a particular, weird way. Compilers assume you will not invoke it. Hence this example: #include <stdio.h> int main() { for(int i; i < 10; i++) { printf("%d\n", i); } return 0; } Which a real compiler compiles to this: #include <stdio.h> int main() { return 0; } Why? Because usage of an uninitialized variable is UB. UB is something a programmer doesn't intend to invoke. Therefore the compiler concludes "(undefined)i < 10" never happens. Therefore the loop never runs. Therefore the loop can be removed. UB is not just non-portable, modern compilers treat it as a weird, radioactive state they avoid touching.
- Kamq 3y agoI acknowledge all of your points, and agree to everything you've said. Except for the actual point that the compiler not being bound by any promise means you should never do it. If you want to walk it back to "it's very rare that intentionally using UB is a good idea", I could agree with that. But never is a bridge too far.
- dale_glass 3y ago> Except for the actual point that the compiler not being bound by any promise means you should never do it. > If you want to walk it back to "it's very rare that intentionally using UB is a good idea", I could agree with that. But never is a bridge too far. No. The point of writing code is having the computer do the thing you want. If there's no predictable outcome, then it should never be done. BTW, the outcome can also change depending on optimization level. So this can produce code that works in debug mode and breaks in release mode or viceversa. If there's something I don't ever look forward to is a program that's impossible to debug because debugging changes its behavior.
- Kamq 3y ago> The point of writing code is having the computer do the thing you want. Correct. We definitely agree here. Most importantly, we didn't talk about the specification at all in this sentence. The behavior of the computer is what we care about. And UB only exists in the spec, it's not a thing compilers or computers do (it's an absence of specification about what a theoretical compiler would do). > If there's no predictable outcome, then it should never be done. I also agree here. But, with a particular compiler there is almost always a predictable outcome. > BTW, the outcome can also change depending on optimization level. And here, I think you're actually being too conservative. It can change with much more than just the optimization level. If you're doing intentional UB, you'd want to freeze not only your optimization level, but your entire flag set. And changing any of them could lead to months of debugging and fixing things. This trade-off is almost never worth it. But I maintain that the "almost" belongs in that sentence.