C/C++ Arena

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:

#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 task

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.

Challenge 2

Write int checked_mul(int a, int b, int *out). If the product fits in an int, store it and return 1. Otherwise return 0 and leave *out alone. As in this step, don't compute the product in a wider type; check with division before multiplying.

Challenge 3

Write int parse_int(const char *s, int *out) that accepts only a complete decimal integer that fits in an int, like "42" or "-17". Store it and return 1, or return 0 (leaving *out alone) for anything else: empty strings, junk after the number, or values out of range. Use strtol and check errno for ERANGE.

Previous: Unsigned wraparound Next: Fixed-width types