3 ms·
This is the trap with 'undefined behaviour': it has nothing to do with portability, but it is a language level definition. I.e., if the C std says it's 'undefi
by beeforpork 6y ago
This is the trap with 'undefined behaviour': it has nothing to do with portability, but it is a language level definition.
I.e., if the C std says it's 'undefined', it is not to be avoided for portability reasons (hardware, assembler), but it must not be used, end of story. The portability stuff is called 'implementation defined' in C, not 'undefined behaviour'. The problem is that the compiler can (and will!) exploit undefined behaviour rules. E.g., the following code is officially broken (and not just on weird hardware, but everywhere, as defined by the C std):
int saturated_increment(int i)
{
if ((i + 1) < i) { /* if it overflows, do not inc */
return i;
}
return i + 1;
}
The compiler may (and many will) remove the whole if() block, because i+1<i is trivially false, because int cannot overflow (says the C standard).
As one can imagine, when compilers started exploiting this, a lot of discussion about sensibility followed. And gcc added -fwrapv among other things.
(And the code would be fine if 'unsigned' was used in stead of 'int', because this is only a problem of signed ints.)
- dkersten 6y ago"Undefined behavior" really means that the standard doesn't define what should happen and that the compiler is therefore free to do whatever it pleases, under the assumption that such code will never occur. Reminds me of the examples where the code gets compiled in a way where a branch that returns from the function is unintuitively always taken because the compiler was able to detect that there is undefined behavior later in the function and since undefined behavior isn't legal, it assumed that it therefore can never reach there, so the branch must always get taken and the actual condition check got optimised away (IIRC). So yeah, undefined behavior isn't "implementation defined" nor "unportable" but rather "illegal not allowed wrong code".
- MaxBarraclough 6y ago> So yeah, undefined behavior isn't "implementation defined" nor "unportable" but rather "illegal not allowed wrong code". There are edge-cases even there. Calling a function generated by a JIT compiler is undefined behaviour, but there's a gentleman's agreement that the compiler won't screw it up for you. Almost all C/C++ compilers promise that floating-point division-by-zero results in NaN (the IEEE 754 behaviour), but according to the C/C++ standards themselves, it's undefined behaviour. You're right though that in general, one should not be complacent about UB.
- giomasce 6y ago> There are edge-cases even there. Calling a function generated by a JIT compiler is undefined behaviour, but there's a gentleman's agreement that the compiler won't screw it up for you. Though you're not writing C/C++ in that case. You're writing "C/C++ for that particular architecture, ABI, OS and compiler". In general C/C++, if your code is correct every present and future, known and unknown compiler is supposed to generate a correct executable. If they don't, they have a bug. You can pretend to be smarter and go UB, but then the responsibility shifts on you, you have (in principle) to validate each compiler and environment and you can claim no bug on anybody other than you.
- MaxBarraclough 6y agoSounds right. If you're doing floating-point work it's not generally a problem to assume that division by zero will result in NaN. Virtually all C and C++ compilers commit to this behaviour in the name of IEEE 754 compliance (even if the IEEE 754 compliance is incomplete).