13.2.4 Practical Advice for Signed Overflow Issues

Ideally the safest approach is to avoid signed integer overflow entirely. For example, instead of multiplying two signed integers, you can convert them to double-width integers, multiply the wider values, then test whether the result is in the narrower range. Or you can use more-complicated code employing unsigned integers of the same width.

Rewriting code in this way will be inconvenient, though, especially if the signed values might be negative and no wider type is available. Using unsigned arithmetic to check for overflow is particularly painful to do portably and efficiently when dealing with an integer type like uid_t whose width and signedness vary from platform to platform. Also, this approach may hurt performance.

Hence it is often useful to maintain code that needs wraparound on overflow, instead of rewriting the code. The rest of this section attempts to give practical advice for this situation.

To detect integer overflow portably when attempting operations like sum = a + b, you can use the C23 <stdckdint.h> macros ckd_add, ckd_sub, and ckd_mul. The following code adds two integers with overflow wrapping around reliably in the sum:

#include <stdckdint.h>
...
/* Set sum = a + b, with wraparound.  */
if (ckd_add (&sum, a, b))
  /* 'sum' has just the low order bits.  */;
else
  /* 'sum' is the correct answer.  */;

To be portable to pre-C23 platforms you can use Gnulib’s stdckdint module, which emulates this part of C23 (see Gnulib). Invoking the stdckdint macros typically costs just one machine instruction for the arithmetic and another instruction for the rare branch on overflow.

If your code uses a signed loop index, make sure that the index cannot overflow, along with all signed expressions derived from the index. Here is a contrived example of problematic code with two instances of overflow.

for (int i = INT_MAX - 10; i <= INT_MAX; i++)
  if (i + 1 < 0)
    {
      report_overflow ();
      break;
    }

Because of the two overflows, a compiler might optimize away or transform the two comparisons in a way that is incompatible with the wraparound assumption.

If your code is intended to be compiled only by GCC and assumes wraparound behavior, and you want to insulate it against any GCC optimizations that would fail to support that behavior, you should use GCC’s -fwrapv option, which causes signed overflow to wrap around reliably (except for division and remainder, as discussed in the next section).

If you need to write portable code and therefore cannot assume that signed integer overflow wraps around reliably, you should consider debugging with a GCC option that causes signed overflow to raise an exception. These options include -fsanitize=undefined and -ftrapv.