Why You Should Almost Always Use Unsigned Integers in C and C++

Almost Always Unsigned

I argue that the fear of unsigned integers is misplaced, as most program values like array indices are never negative. While guides like the Google C++ Style Guide discourage them, I show how proper idioms handle underflow safely. Using unsigned types often prevents undefined behavior found in signed arithmetic, making code safer across languages like C, C++, Go, Rust, and Odin.

The most typical argument against the use of unsigned integers is that it's more error prone since it's far easier for an expression to underflow than it is to overflow.
  1. delta_p_delta_x

    I have a better solution: address the root cause of unsafe semantics by not using raw indexed for-loops, unless one absolutely needs an index, in which case one should generate it with std::views::enumerate. To reverse it, use std::views::enumerate | std::views::reverse. Ditto for languages with similar semantics.

    I almost never write a raw for-i loop any more, especially since 99% of the time I want to enumerate through the entire array or vector, and I can just use a ranged for-loop to do that. It allows me to redesign my code around the data, express things at a higher level, and my code looks far more SIMDable and reminiscent of array programming languages. And yet it is safer, I will never see under/overflow or any of these old-hat problems.

    If you are using C, then too bad, you're stuck with a language that doesn't allow the programmer to more meaningfully and more clearly express intent at a higher level of abstraction without paying additional runtime costs.

    This stuff compiles to broadly the same assembly.

  2. dataflown

    Stroustrup recommends int over unsigned. Dijkstra recommends int over unsigned. Google coding guidelines recommend int over unsigned.

    Blogger recommends unsigned over int.

    Tough choice.

  3. shaggie76

    While I'm a fan of unsigned (size_t mostly) there have been a few times when the tax for converting them to float was shockingly high:

    https://godbolt.org/z/96T4jTshc

    1-2 instructions for signed vs 11 including a branch for unsigned.

    (in times like these I found casting to signed first preferable)

  4. uecker

    A long time ago, I also thought one should use unsigned mostly but I am now in the opposite camp. Unsigned integers in C have semantics for modulo arithmetic. They are suitable if you need this, so for crypto, hashes, or if you only care about bits, etc. IMHO they should not be used for anything else. The reason is that it is very easy to screen for signed overflow bugs exactly because they have undefined behavior, just by turning on a sanitizer. It is also possible to transform overflow to safe traps at run-time where this is important. In contrast, finding unsigned wraparound bugs is extremely hard and can not be done automatically and preventing consequences of such bugs is difficult. Also any kind of index computation may have intermediate results that may be negative, so signed arithmetic is also generally more useful and far easier for people to understand and get right.

  5. kazinator

    In a language that has arbitrary precision integers, you'd pretty much never want them unsigned, or even to have signed and unsigned flavors.

    Whether unsigned or signed is better is a matter that is a combination of personal opinion and the quirks of a given systems programming fixed integer language.

    The trade-off reasoning would be different, for instance, in a language that requires implementations to provide two's complement signed integers, with wraparound semantics. Or, say, no wraparound semantics but a robust overflow detection system coupled to exception handling.

    There are other matters beside overflow, like conversions. In C, mixtures of signed and unsigned bring in some implementation-defined conversion rules, which nudges the argument toward "all unsigned" or "all signed" for the sake of avoiding mixtures.

    I like to trot out the following argument.

    Suppose a, b and c are small integers close enough to zero that any additive/subtractive combination of them is free of overflow.

    If they are signed, then we can make inequality derivations like

    a + b < c

    b < c - a // subtract a from both sides; "bring to other side"

    If they are unsigned, then we cannot do this. That is a barrier to refactoring code with arithmetic conditionals and just reasoning about it.

More from this day

2026-07-08