Целочисленный тип (integer type) хранит целое число в фиксированном количестве байт. Размер определяет диапазон: чем больше байт, тем шире диапазон и тем больше памяти занимает переменная.

Какие бывают

ТипГарантия стандартаОбычно на x86-64 LinuxДиапазон (типично)
char≥ 8 бит1 байт−128…127
short≥ 16 бит2 байта−32 768…32 767
int≥ 16 бит4 байта−2 147 483 648…2 147 483 647
long≥ 32 бита8 байт±9.2·10¹⁸
long long≥ 64 бита8 байт±9.2·10¹⁸

Стандарт задаёт только минимумы, а не точные размеры: long на Windows — 4 байта, на Linux — 8. Полагаться на конкретное число байт нельзя, это частый источник кода, который «работает только у меня». Размеры упорядочены от short к long long, но соседние типы могут совпадать: слово long не гарантирует, что тип шире int именно на вашей системе.

У каждого типа есть знаковый и беззнаковый вариант. int, signed int и просто signed — один и тот же тип; unsigned int (или просто unsigned) — соответствующий беззнаковый. Для знакового целого ширины N диапазон это −2^(N−1) … 2^(N−1) − 1, для беззнакового 0 … 2^N − 1. Беззнаковый вариант не получает больше памяти: у него то же число представимых значений, только без отрицательной половины.

Узнать фактические значения:

#include <iostream>
#include <limits>
 
int main() {
  std::cout << sizeof(int) << '\n';                          // размер в байтах
  std::cout << std::numeric_limits<int>::max() << '\n';      // верхняя граница
  return 0;
}

Когда размер важен точно — есть типы фиксированной ширины из <cstdint>:

#include <cstdint>
 
std::int32_t count = 0;
std::int64_t total = 0;
std::uint32_t mask = 0;

Число в имени — ширина в битах; такие же имена есть для 8, 16 и 64 бит. Типы точной ширины предоставляются, когда у реализации есть подходящие целочисленные типы.

Суффиксы литералов

У самого числа в коде тоже есть тип. По умолчанию целый литерал — int, а если число в int не помещается, компилятор берёт следующий подходящий тип. Суффикс задаёт тип явно:

10     // int
10U    // unsigned int
10L    // long
10UL   // unsigned long
10LL   // long long
10ULL  // unsigned long long

Регистр значения не имеет, но суффиксы пишем заглавными: строчная l слишком похожа на единицу. Полный разбор — Литералы.

У short собственного суффикса нет: short x = 1; создаёт переменную типа short, но литерал 1 остаётся литералом типа int и преобразуется при инициализации.

Суффикс — не украшение, он меняет результат вычисления. Классический случай — переполнение в промежуточном значении:

long long a = 1000000 * 1000000;    // оба литерала int: переполнение
long long b = 1000000LL * 1000000;  // вычисление идёт в long long

В первой строке умножение выполняется в int, переполняется — и только потом испорченный результат попадает в long long. Тип переменной слева на порядок вычисления не влияет, важны типы операндов. Переполнение знакового целого — Неопределённое поведение, а не «просто отрицательное число».

Малые типы в арифметике не участвуют

char, short и bool перед операцией продвигаются до int:

short x = 1;
short y = 2;
std::cout << sizeof(x) << '\n';       // 2
std::cout << sizeof(x + y) << '\n';   // 4

Сумма двух short имеет тип int, а не short. Правила целиком — Арифметические преобразования типов.

Тонкости

  • Знаковость char не определена стандартом: на одних платформах он знаковый, на других нет. Для чисел используют signed char или unsigned char, а char оставляют для символов — см. Символьный тип char.
  • sizeof(char) равен 1 по определению, но «1 байт» не обязан быть 8 битами (на учебных платформах — всегда 8).
  • Диапазон несимметричен: отрицательных значений на одно больше, чем положительных, потому что ноль занимает место в положительной половине.

Частые ошибки

  • Считать, что int всегда 4 байта, а long — 8.
  • Забыть суффикс при работе с большими числами и получить переполнение до присваивания.
  • Использовать int для размеров и индексов больших контейнеров, где нужен 64-битный тип.

Рядом: Беззнаковые типы, Типы с плавающей точкой, Целочисленное деление и остаток.