Step 3 of 8
Signed overflow is undefined
For signed integers, going past INT_MAX (or below INT_MIN) is undefined behavior (UB). "Undefined" doesn't mean "wraps around like unsigned". It means the C standard places no requirements at all on what happens, and the compiler is allowed to assume it never occurs.
That assumption is used by optimizers. For example, a compiler may delete a check like if (x + 1 < x) entirely, reasoning that in a correct program x + 1 can never be less than x. So "detect overflow after it happened" doesn't work: the detection code itself can vanish.
Check before, with operations that can't overflow
To know whether a + b would overflow, rearrange the question so no step can overflow:
- If
b > 0, the sum overflows upward exactly whena > INT_MAX - b. (INT_MAX - bis safe becausebis positive.) - If
b < 0, it overflows downward exactly whena < INT_MIN - b.
#include <stdio.h>
#include <limits.h>
int checked_mul(int a, int b, int *out) {
long long wide = (long long)a * b;
if (wide > INT_MAX || wide < INT_MIN) {
return 0;
}
*out = (int)wide;
return 1;
}
int main(void) {
int r = 0;
int ok = checked_mul(50000, 50000, &r);
printf("%d %d\n", ok, r);
ok = checked_mul(-300, 7, &r);
printf("%d %d\n", ok, r);
return 0;
}
0 0
1 -2100
This example checks multiplication a different way: doing the math in a wider type (long long holds any product of two ints) and then checking the range. That works when a wider type exists; the rearranged comparisons work even for the widest type.
Leaving *out untouched on failure lets the caller rely on its previous value. (GCC and Clang also offer __builtin_add_overflow, and C23 standardizes ckd_add in <stdckdint.h>.)
Your turn: write int checked_add(int a, int b, int *out). If the sum fits, store it in *out and return 1. Otherwise return 0 and leave *out untouched.