4 ms·
Where exactly does C standard explicitly say it can do so? UB was introduced to allow skipping checks that otherwise might have added overhead, like array boun
by pkolaczk 9y ago
Where exactly does C standard explicitly say it can do so?
UB was introduced to allow skipping checks that otherwise might have added overhead, like array bound checks or null checks or to give more flexibility to how correct programs are optimized (e.g. freedom to evaluate arguments in any order). Therefore each compiler/library/os vendor could implement these cases differently.
However, it was never meant to allow compiler to assume "ok, I can see your code is totally broken, so let's break it more, cause you don't care anyway".
- steveklabnik 9y agohttp://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf is the draft of the C11 spec, 3.4.3 undefined behavior behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). ------------- Even if you want to argue over what "imposes no requirements" means, I'd argue that "ignoring the situation completely with unpredictable results" is very clear. The note also says (and elsewhere in the standard it reinforces this) that since there are no requirements, implementations could absolutely do things like perform runtime checks, etc. Compilers do not do this though. This isn't for no reason, but that's a separate issue.
- loup-vaillant 9y ago> However, it was never meant to allow compiler to assume "ok, I can see your code is totally broken, so let's break it more, cause you don't care anyway". That's nevertheless how compiler writers are interpreting the standard right now. Take integer overflow for instance. Stuff like this: unsigned u = some_computation(); int x = u; // because of reasons x += 42; // hmm this may overflow, let's check that if (x < 0) { // It's a 2's complement machine, this'll work handle_overflow(x); // now, let's deal with the overflow } Now here's how your optimising compiler see that stuff: unsigned u = some_computation(); int x = u; // x is always positive. x += 42; // signed integer never overflow if (x < 0) { // x was positive, no overflow... always false handle_overflow(x); // Let's delete this dead code. } Then, what actually happens: unsigned u = some_computation(); int x = u; x += 42; This is insane.
- deleted 9y ago[deleted]
- smitherfield 9y agoA correction: a compiler would not be able to assume that `x` is always positive because casts between signed and unsigned integer types are based on their binary representation (what computers see), not their value (what humans see).[1] [1] https://wandbox.org/permlink/dDDAJjOlm50GD1PW https://wandbox.org/permlink/dDDAJjOlm50GD1PW It would be able to (correctly) make that assumption if `x` were a long, assuming 64-bit longs and 32-bit int/unsigneds.
- int_19h 9y agoIn the C standard, a cast from unsigned to signed integer is undefined behavior if it overflows. So the compiler can, indeed, assume that "x" is always positive in the code snippet above - if the value of "u" is such that it would require signed wraparound when initializing "x", the rest of the program is U.B., and so that possibility doesn't even have to be considered.
- smitherfield 9y agoThis is untrue; see section 6.3.1.3: When a value with integer type is converted to another integer type ... [if] the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised. http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1570.pdf http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1570.pdf So casts from unsigned to signed must have a well-defined result, but the implementation can choose how to define it. You could in theory have an implementation that defines that casts from unsigned to signed are always positive,[1] but since that's not how GCC and every other mainstream (existing?) compiler defines it, they are not allowed to assume the result of a cast from an arbitrary unsigned integer to a signed integer of equal width is positive. [1] In which case the compiler would be allowed to assume that unsigned-to-signed casts are never negative. This assumption would however be both safe and correct, and would have nothing to do with UB.
- 9y ago