unsigned integers and modular arithmetic
The simplest way to store a whole number in a fixed number of bits is also the most direct: just write it in binary, the same way you would on paper. An unsigned integer is exactly this — a non-negative whole number stored as its plain binary place-value pattern, with no room or rule for a minus sign. 'Unsigned' literally means 'no sign'; these are counting numbers, starting at zero and going up.
With n bits you can represent the values 0 up to 2^n minus 1, and no others. An 8-bit unsigned integer ranges from 0 to 255; a 16-bit one from 0 to 65535; a 32-bit one from 0 to 4294967295. The bit pattern is read by adding the place values of the set bits, exactly as in ordinary binary: 1111 1111 is 128+64+32+16+8+4+2+1 = 255. Because the width is fixed, there is a hard ceiling, and that ceiling is where the interesting behaviour begins.
When an unsigned result would go past the top of the range, it does not error and it does not become negative — it WRAPS, by definition, as if numbers lived on a clock. Formally, unsigned arithmetic in C is modular: every result is taken modulo 2^n. On 8 bits, 255 + 1 gives 0 (255 + 1 = 256, and 256 mod 256 = 0), and 0 - 1 gives 255. This is not a bug or an accident; the C standard SPECIFIES it, which is exactly why unsigned types are the right tool for hashing, checksums, bit manipulation, and any place you want clean, predictable wraparound. (This is in sharp contrast to signed overflow, which is undefined behavior — never assume signed types wrap the same friendly way.)
An 8-bit unsigned counter at 255, incremented once, becomes 0 — like an odometer rolling over. In C: unsigned char c = 255; c++; now c == 0, guaranteed.
Unsigned arithmetic wraps modulo 2^n, by definition.
Subtracting a larger unsigned value from a smaller one does NOT give a negative number — it wraps to a huge positive one, so a loop like 'for (unsigned i = n; i >= 0; i--)' never ends, because i can never be negative.