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 or 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:
printf("i=%d and is %s\n",i,(i<0 ? "negative":"non-negative"))
can produce “negative” with a negative number or “non-negative” with exactly the same negative number depending on context. Here’s the output from a simple program. The first part is as expected when we first assign INT_MAX to i and then add one:
First test if clang will correctly detect overflow in straight line code
i= -2147483648 and is negative
But then we get to make new discoveries
Now see what happens inside for(i=0; i >=0; i++) Inside loop: i= -2147483648 and is non-negative Done with loop: i= -2147483648 and is non-negative
So the test (i <0) can be false for i equal to -2147483648 or true – depending on context.
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. However, according to the WG14 committee, this chaos 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("i=%d and is %s\n",i,(i<0 ? "negative":"non-negative")) int main(int argc, char **argv){ int i = INT_MAX; int willoverflow = 0; printf("First test if clang will correctly detect overflow in straight line code\n"); i++; whatis(i); printf("\nNow see what happens inside for(i=0; i >=0; i++)\n"); for(i= 0; i >= 0; i++){ if(willoverflow){ printf("Inside loop: "); whatis(i); break; } if(i == INT_MAX){ willoverflow++;} } printf("Done with loop: "); whatis(i); }See also 3 modest proposals for the C standard andDepressing and faintly depressing days for the C Standard.For a more balanced and moderate view, Linus Torvald’s comments on aliasing are good.