Code controlling a device – a medical instrument, a power plant, an airplane, perfectly working and thoroughly tested code can be turned into garbage if someone runs a new version of a C compiler or tells the compiler to optimize. Nobody in the WG14 Standards committee and none of the people managing these compilers seems to have any idea of the dangerous nature of their “undefined behavior” game. For example, Clang “optimizer” discards C semantics to decide that the exact same integer variable, holding the exact same bit pattern is sometimes negative and sometimes positive. The meaning of (i <0) is context dependent. In fact we can run a C program that, “optimized” will tell us:
Now i= -2147483648 i<0 is true Now i= -2147483648 i<0 is false Now i= -2147483648 i<0 is false
So the test (i <0) can be false for i equal to -2147483648 or true – depending on context. The exact same code, optimized at level 1 prints:
Now i= -2147483648 i<0 is true Now i= -2147483648 i<0 is true
There are only two lines, because the middle line in printed above comes in the body of a for loop that is only supposed to execute when i>=0
for(i= 0; i >= 0; i++){ ... }
If you ask the Clang developers or the WG14 C-standards committee, you will probably be told this semantics represents an “optimization”. Certainly the compiled code is able to tell us that -2147483648 is not negative very quickly so that is impressive, I guess. Sometimes people will falsely claim that this behavior is mandated by the ISO Standard. Not so. In fact, the Intel ICC compiler thinks -2147483648 is negative even with all optimizations turned on (see the example). However, according to the WG14 committee, the chaos of gcc/clang is permitted by the standard. A conforming C compiler can produce code in which a test for negative values, sometimes can and sometimes cannot detect that -2147483648 is negative.
To be fair, in this case you can ask the Clang Compiler to give expected C semantics by passing the flag “fwrapv”. But this is certainly not the only place where current and expected C semantics differ and you have to know all the magic flags and hope that they cover all the cases if you depend on Clang to compile C code with C semantics.
Try it for yourself – make sure you use -O3 flags.
#include <stdio.h> #include <stdlib.h> #include <limits.h> #define whatis(i) printf("Now i= %d i<0 is %s\n",i,(i<0 ? "true":"false")) int main(int argc, char **argv){ int i = INT_MAX; int willoverflow = 0; i++; whatis(i); for(i= 0; i >= 0; i++){ if(willoverflow){ whatis(i); break; } if(i == INT_MAX){ willoverflow++;} } whatis(i); }
See also 3 modest proposals for the C standard and Depressing and faintly depressing days for the C Standard.
For a more balanced and moderate view, Linus Torvald’s comments on aliasing are good.