Solution 1:

Think of this function:

int f(int i) {
    return i+1 > i;
}

Mathematically speaking, i+1 should always be greater than i for any integer i. However, for a 32-bit int, there is one value of i that makes that statement false, which is 2147483647 (i.e. 0x7FFFFFFF, i.e. INT_MAX). Adding one to that number will cause an overflow and the new value, according to the 2's compliment representation, will wrap-around and become -2147483648. Hence, i+1>i becomes -2147483648>2147483647 which is false.

When you compile without -fwrapv, the compiler will assume that the overflow is 'non-wrapping' and it will optimize that function to always return 1 (ignoring the overflow case).

When you compile with -fwrapv, the function will not be optimized, and it will have the logic of adding 1 and comparing the two values, because now the overflow is 'wrapping' (i.e. the overflown number will wrap according to the 2's compliment representation).

The difference can be easily seen in the generated assembly - in the right pane, without -fwrapv, function always returns 1 (true).

Solution 2:

for (int i=0; i>=0; i++)
    printf("%d\n", i);

With -fwrapv, the loop will terminate after INT_MAX iterations. Without, it could do anything since undefined behavior is unconditionally invoked by evaluation of i++ when i has the value INT_MAX. In practice, an optimizing compiler will likely omit the loop condition and produce an infinite loop.

Solution 3:

The ISO standard working group WG14 exists to establish a convention that all C compilers must adhere to. Some compilers may (and do) also implement extensions. According to ISO standard C, those extensions are considered one of the following:

  • implementation-defined, meaning the compiler devs must make a choice, document that choice and maintain the lot in order to be considered a compliant C implementation.
  • C11/3.4.3 establishes a definition for undefined behaviour and gives an extremely familiar example, which is vastly superior to anything I could write:

    1 undefined behavior behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

    2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

    3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.


There's also an unspecified behaviour, though I'll leave it as an exercise to you to read about that in the standard.

Be careful where you tread. This is one of the few generally accepted undefined behaviours where it's typically expected that a LIA-style wrapping will occur upon a twos complement representation without a trap repesentation. It's important to realise that there are implementations that use a trap representation corresponding to the bit representation containing all ones.

In summary, fwrapv and ftrapv exist to pass on a choice to you, a choice which the developers would have otherwise had to make on your behalf, and that choice is what happens when signed integer overflow occurs. Of course, they must select a default, which in your case appears to correlate to fwrapv rather than ftrapv. That needn't be the case, and it needn't be the case that these compiler options change anything what-so-ever.