I need help understanding something about overflow with signed integers. I have read in this post Wrap around explanation for signed and unsigned variables in C? that C language (or at least some C compilers) have something called "Undefined behaviour" as a result of overflow with signed integers. In this post, people said "The GCC compiler assumes that overflow for signed integers never occur so that the compiler can optimize"; other people said "You can't rely on wraparound at the time when working with signed integers".
I have used Dev cpp, but I'm not sure if this IDE works with GCC so I installed Code Blocks and now I'm sure it works with GCC (at least in my config), and I overflowed a signed integer variable to experiment with the things the people said, but I found that when it overflows, the IDE doesn't show an error or a warning and the signed integer shows wraparound behaviour. So, can you help me to clarify this situation?
Also I want to ask you for help about the concept "Strict overflow" and the "option" -Wstrict-overflow
.
CodePudding user response:
… the signed integer shows a wrap around behaviour…
Here is an example where GCC and Clang do not show wraparound behavior:
#include <limits.h>
#include <stdio.h>
void foo(int x)
{
if (x - INT_MAX <= 0)
printf("True.\n");
else
printf("False.\n");
}
If x - INT_MAX
wrapped around, and this routine were called with −2 for x
, then x - INT_MAX
would wrap around to INT_MAX
. (For example, if INT_MAX
is 231−1, then −2 − (231−1) = −231−1, and wrapping it modulo 232 gives −231−1 232 = 231−1. Then x - INT_MAX
would be positive, so x - INT_MAX <= 0
would be false.) So the routine could print “False.” some of the times it is called.
However, when we compile it with GCC and -O3
, we see the compiler has optimized it to code that only prints “True.” This shows the compiler is not assuming that arithmetic wraps.
The compiler, or its writers, can reason:
- If
x - INT_MAX
does not overflow, then it must give a result less than or equal to zero, because there is noint
value forx
that is greater thanINT_MAX
. In this case, we must executeprintf("True.\n");
. - If
x - INT_MAX
does overflow, then the behavior is not defined by the C standard. In this case, we can execute any code we desire, and it is easier for optimization to execute the same code as the other case,printf("True.\n");
.
This is equivalent to reasoning:
x - INT_MAX
does not overflow. Therefore, it is less than or equal to zero, sox - INT_MAX <= 0
is always true, andprintf("True.\n");
is always executed. So we can discard theelse
case.
GCC and Clang have a switch -fwrapv
, that extends the C standard by defining addition, subtraction, and multiplication of signed integers to wrap. When we compile with this switch, we can see the above reasoning no longer applies. It is possible for x - INT_MAX <= 0
to be false, and so the compiler generates both code paths.