Integer Variants: Size and Sign
Choosing how to store a whole number is a decision. Some values can reach into billions. Others are small and don’t require a lot of memory. Still others can never be negative.
C++ gives you multiple types of integers. You don’t need to memorize all of them today, but you should understand the differences and know where to look when the default isn’t enough.
Integer types and sizes
int is the default integer type, but C++ gives you smaller and larger options depending on how big your values can get.
| Type | Typical Size | Signed Range (approx) | Unsigned Range (approx) |
|---|---|---|---|
short | 2 bytes | −32K to 32K | 0 to 65K |
int | 4 bytes | −2 billion to 2 billion | 0 to 4 billion |
long | 4 or 8 bytes | varies by platform | varies by platform |
long long | 8 bytes | −9 quintillion to 9 quintillion | 0 to 18 quintillion |
The pattern is straightforward: a larger type uses more bytes, which means more bits, which means a wider range of values. Most C++ code skips long in favour of int or long long.
The LL after 8100000000 is a hint to the compiler to treat this number as a long long. Without it, the compiler infers the type from the value itself. The LL makes your intent explicit.
You’ll encounter suffixes like these formally in a later lesson.
Signed and unsigned integers
All four types in the table above are signed by default. “Signed” just means the type is capable of representing negative values.
Each of these types also has an unsigned variant, which can only hold zero and positive values. In exchange for giving up negatives, it’s possible to store larger values in the variable.
The reason is mechanical: computers store numbers in binary (a sequence of bits, each 0 or 1), and a signed integer has to dedicate one of those bits to recording whether the number is negative. An unsigned integer puts that bit to good use by storing part of the value instead, which means it can hold roughly twice the maximum value.
To declare an unsigned integer, place the unsigned keyword before the type name, as shown on line 5:
Overflow: when a value outgrows its type
If you try to store a value that’s too large for the type, the value overflows. For unsigned types, it wraps around predictably (like an odometer rolling over). For signed types, the result is unpredictable. In practice it usually also wraps around, but you can’t rely on it:
The value wrapped all the way from the maximum to the minimum. The solution to this problem is simple: if your values might exceed a type’s range, use a larger type.
How to guarantee exact sizes
The table above shows typical sizes, and they hold on most modern desktop and server hardware. But the C++ standard deliberately leaves room for variation. On small embedded systems, int might be smaller.
This rarely affects everyday code, but it matters when another system expects a specific number of bytes. A network packet, for instance, might expect precisely 32 bits, not “probably 32 bits.”
For those situations, <cstdint> gives you types with exact, guaranteed sizes:
The naming pattern is:
int+ width +_tfor signed types:int8_t,int16_t,int32_t,int64_tuint+ width +_tfor unsigned types:uint8_t,uint16_t,uint32_t,uint64_t
Here width means number of bits. The ULL suffix on line 6 works the same way as LL, but signals the value is an unsigned long long.
For most everyday work you don’t need these types.
The std::size_t type
There is one more type worth knowing about, as it appears frequently in standard library code.
std::size_t is an unsigned integer type whose width is chosen automatically by the platform. The standard library uses it for maintaining counts and sizes, since it’s guaranteed to be the right size regardless of the target platform.
Choosing the right type
Most of the time, the choice comes down to a few simple rules:
intfor everyday whole numbers. It’s the size the CPU handles most naturally.long longwhen values might exceed two billion (population figures, file sizes, etc.).unsignedwhen a value is conceptually non-negative.int32_tand friends when the exact size matters for correctness across platforms.std::size_twhen working with sizes and counts, especially when interfacing with standard library functions.