Per paragraph 6.5/5 of both C99 and C11, evaluation of an expression produces undefined behavior if the result is not a representable value of the expression's type. For arithmetic types, that's called an overflow. Unsigned integer arithmetic does not overflow because paragraph 6.2.5/9 applies, causing any unsigned result that otherwise would be out of range to be reduced to an in-range value. There is no analogous provision for signed integer types, however; these can and do overflow, producing undefined behavior. For example,
#include <limits.h> /* to get INT_MAX */
int main(void) {
int i = INT_MAX + 1; /* Overflow happens here */
return 0;
}
Most instances of this type of undefined behavior are more difficult to recognize or predict. Overflow can in principle arise from any addition, subtraction, or multiplication operation on signed integers (subject to the usual arithmetic conversions) where there are not effective bounds on or a relationship between the operands to prevent it. For example, this function:
int square(int x) {
return x * x; /* overflows for some values of x */
}
is reasonable, and it does the right thing for small enough argument values, but its behavior is undefined for larger argument values. You cannot judge from the function alone whether programs that call it exhibit undefined behavior as a result. It depends on what arguments they pass to it.
On the other hand, consider this trivial example of overflow-safe signed integer arithmetic:
int zero(int x) {
return x - x; /* Cannot overflow */
}
The relationship between the operands of the subtraction operator ensures that the subtraction never overflows. Or consider this somewhat more practical example:
int sizeDelta(FILE *f1, FILE *f2) {
int count1 = 0;
int count2 = 0;
while (fgetc(f1) != EOF) count1++; /* might overflow */
while (fgetc(f2) != EOF) count2++; /* might overflow */
return count1 - count2; /* provided no UB to this point, will not overflow */
}
As long as that the counters do not overflow individually, the operands of the final subtraction will both be non-negative. All differences between any two such values are representable as int
.