Целочисленный тип (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-битный тип.
Рядом: Беззнаковые типы, Типы с плавающей точкой, Целочисленное деление и остаток.