CppType Modifiers

Type Modifiers

C++ lets you refine the fundamental integer types with **modifiers** that change their range or sign behavior: signed, unsigned, short, long, and long long. These modifiers combine with a base type (usually int or char) to describe exactly how a number should be stored.
Signed vs Unsigned
  • signed — can represent negative and positive numbers (this is the default for int)

  • unsigned — can only represent zero and positive numbers, but doubles the positive range for the same number of bits

Size Modifiers
  • short — requests a smaller integer, typically 2 bytes

  • long — requests a wider integer, typically 4 or 8 bytes depending on platform

  • long long (C++11+) — guaranteed at least 8 bytes

Valid Combinations

Declaration

Typical Size

Range (typical)

short int / short

2 bytes

-32,768 to 32,767

unsigned short

2 bytes

0 to 65,535

int

4 bytes

-2,147,483,648 to 2,147,483,647

unsigned int

4 bytes

0 to 4,294,967,295

long int / long

4 or 8 bytes

platform-dependent

unsigned long

4 or 8 bytes

platform-dependent

long long

8 bytes

-9.2 x 10^18 to 9.2 x 10^18

unsigned long long

8 bytes

0 to 1.8 x 10^19

Note
Modifiers can be combined and reordered fairly freely — unsigned long long int, long long unsigned, and unsigned long long all mean the same thing. Omitting int is common and legal: unsigned alone means unsigned int.

CPP
#include <iostream>

int main() {
    short int smallNum = 100;
    unsigned int positiveOnly = 4000000000u;
    long long bigNum = 9000000000000000000LL;

    std::cout << smallNum << " " << positiveOnly << " " << bigNum << std::endl;
    return 0;
}
Unsigned Overflow Wraps Around
Unsigned wraps; signed overflow is undefined behavior
Unsigned integer arithmetic is defined by the standard to wrap around using modular arithmetic — subtracting past zero wraps to the maximum value instead of going negative. Signed integer overflow, by contrast, is **undefined behavior**: the compiler is allowed to assume it never happens, which can produce surprising results (including the optimizer removing checks you expected to run).

CPP
#include <iostream>

int main() {
    unsigned int u = 0;
    u = u - 1;                  // wraps around: becomes 4,294,967,295 (well-defined)
    std::cout << u << std::endl;

    int s = 2147483647;         // INT_MAX
    s = s + 1;                  // undefined behavior — do NOT rely on this wrapping
    std::cout << s << std::endl;
    return 0;
}
When to Use Unsigned vs Signed
Use unsigned types for values that can never be logically negative and where you want the extra positive range — sizes, counts, array indices from library functions like std::vector::size(). Prefer signed types for general arithmetic, especially anything involving subtraction that might legitimately go negative (like differences between positions or scores).
Mixing signed and unsigned is a common bug source
When a signed value is compared with an unsigned value in an expression, the compiler implicitly converts the signed value to unsigned. A negative number then becomes a huge positive number, silently breaking the comparison.

CPP
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v{1, 2, 3};
    int i = -1;

    // v.size() returns an unsigned type (size_t); i is converted to unsigned,
    // becoming a huge number, so this comparison is TRUE when it looks like it
    // should be false:
    if (i < v.size()) {
        std::cout << "This prints, surprisingly!" << std::endl;
    }
    return 0;
}