Consider the following code:

$ cat o.c 
#include <stdio.h>
#include <limits.h>

int absolute(int i) {
  int j = i < 0 ? -i : i;
  if (j<0)      /* This is line 6 */
    return 0;
  return j;
}

int main() {
  int i = 1;
  printf("%d %d\n", i, absolute(i));
  return 0;
}

Compiling it with -O2 and -Wstrict-overflow produces a warning:

$ gcc -O2 -Wall -Wextra -Wstrict-overflow o.c 
o.c: In function ‘absolute’:
o.c:6:6: warning: assuming signed overflow does not occur when simplifying comparison of absolute value and zero [-Wstrict-overflow]

Now consider the following which seems functionally equivalent to the above code:

$ cat p.c 
#include <stdio.h>
#include <limits.h>

int main() {
  int i = 1;
  int j = i < 0 ? -i : i;
  if (j<0) // Changing i to INT_MIN above and changing (j<0) to (j>INT_MAX)
           // doesn't change the behavior
    j=0;
  printf("%d %d\n", i, j);
  return 0;
}

Compiling this with the same options doesn't result in any warnings.

$ gcc -O2 -Wall -Wextra -Wstrict-overflow p.c 
$

As commented in the second code, changing the assignment to i=INT_MIN; and the condition to (j>INT_MAX) doesn't warn either.

I'm using gcc 4.7.2 on Ubuntu:

$ gcc --version
gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2

I'm unable to figure the difference in the two cases. Does it have something to do with optimization or is gcc not behaving quite right here?

有帮助吗?

解决方案

The two snippets are different from the point of view of the compiler's intra-procedural analyses for optimization.

In the first one, GCC knows and tells you that it is going to compile function absolute() into something that may differ from what you expected if you believed that signed overflow always produces two's complement results (It doesn't. Signed overflow is undefined behavior. GCC can choose any behavior it wishes for the inputs to absolute() that cause signed overflow. In this case, the compiler is going to remove if (j<0) … as dead code, which is the correct behavior for the defined inputs)

In the second snippet, there is no such possible misunderstanding because -i never overflows (since i can be seen to be 1 with only a local value analysis). All behaviors of the function are defined, there is no cause for disagreement between GCC and the programmer, and no reason to emit a warning.

Then, there is the fact that GCC does not warn for i=INT_MIN;. This is probably caused by the order in which passes are applied. I would bet that a constant propagation pass has been applied first, so that a value(*) for the condition j<0 has already been computed at the time when the sophisticated optimizing pass that emits the warning is applied. This latter pass does not see the comparison so it has no reason to warn.

If you wish signed overflow to always produce two's complement results, you can use gcc -fwrapv -fno-strict-overflow. Ian Lance Taylor alludes to these options in his post but he does not make it clear why a second option was necessary to really make GCC behave when there was already one that should have done this job. Using these options should make the warning “assuming signed overflow does not occur” disappear.

(*) I should insist that this is a value for the condition. Again, the compiler may choose any value it wants because computing -INT_MIN is undefined behavior on this architecture. The constant propagation pass may apply two's complement but it does not have to.

其他提示

In the first program the optimizer recognizes the sign of an absolute value is checked with a variable argument not known at compile time.

In the second program, the value of i is known and the result of j and j < 0 are computed at compile time before any further optimizations like the one used in the first program.

A good way to get convinced of this is to change the value of i in the second program to something known only at run-time:

int i = 1 + printf(""); // 1 if no error, cannot be deduced
                        // at compile time
int j = i < 0 ? -i : i;
if (j<0) 

would get you the same warning as in the first program when compiled with -O2 -Wstrict-overflow.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top