Что стандарт C++ говорит о размере типов int, long и long long?
Гарантии стандарта C++: минимальные размеры и иерархия
Стандарт C++ (начиная с C++98 и до C++23) не фиксирует точные размеры для типов int, long и long long. Вместо этого он устанавливает минимальные диапазоны значений и отношения порядка между типами. Это сделано для того, чтобы компиляторы могли оптимально использовать архитектуру процессора.
Что гарантирует стандарт?
- Иерархия размеров:
sizeof(short) ≤ sizeof(int) ≤ sizeof(long) ≤ sizeof(long long) - Минимальные размеры в битах:
shortиint: не менее 16 бит (2 байта)long: не менее 32 бит (4 байта)long long: не менее 64 бит (8 байт)
- Диапазоны значений (из
):INT_MIN= -32767 минимум,INT_MAX= 32767 минимум (на практике -2³¹ и 2³¹-1)LONG_MIN/LONG_MAX: минимум -2³¹+1 / 2³¹-1LLONG_MIN/LLONG_MAX: минимум -2⁶³+1 / 2⁶³-1
Почему на Windows и Linux разные результаты?
Это связано с моделями данных, принятыми в операционных системах:
- ILP32 (32-битные системы):
int,long, указатели — 32 бита. Пример: старые Windows, Linux x86. - LP64 (Unix-подобные 64-битные):
int= 32 бита,longи указатели = 64 бита. Используется в Linux, macOS. - LLP64 (Windows 64-bit):
intиlong= 32 бита,long longи указатели = 64 бита. Вот почему на вашей Windowslong= 4 байта.
Как писать переносимый код?
Используйте фиксированные типы из (C++11+):
#include <iostream>
#include <cstdint>
int main() {
int32_t exactly_32_bits; // всегда 4 байта
int64_t exactly_64_bits; // всегда 8 байт
int_least32_t at_least_32; // минимум 32 бита
int_fast32_t fast_32; // самый быстрый тип не менее 32 бит
std::cout << "int32_t: " << sizeof(int32_t) << " bytes\n";
std::cout << "int64_t: " << sizeof(int64_t) << " bytes\n";
return 0;
}
Проверка на этапе компиляции
Используйте static_assert для контроля размеров:
#include <cstdint>
static_assert(sizeof(int) == 4, "int must be 4 bytes");
static_assert(sizeof(long long) == 8, "long long must be 8 bytes");
Практические рекомендации
- Для циклов и индексов: используйте
intилиsize_t— это оптимальный размер для платформы. - Для хранения больших чисел:
int64_tилиlong long. - Для совместимости с API: используйте типы, которые ожидает API (например,
longв WinAPI). - Избегайте
longв новом коде — его размер слишком непредсказуем.
Запомните главное правило: никогда не полагайтесь на конкретный размер базовых типов. Всегда используйте sizeof() для проверки или <cstdint> для точных типов. Это сделает ваш код переносимым между Windows, Linux, macOS и встраиваемыми системами.
Дополнение: исторический контекст и практические советы
Проблема с размерами типов берёт начало из 70-х годов, когда C создавался для PDP-11 (16-битный int). С тех пор архитектуры эволюционировали, а стандарт сохранил гибкость. Вот что ещё важно знать:
Почему стандарт не фиксирует размеры?
- Производительность: на 32-битных ARM выгодно иметь 32-битный
long, на 64-битных RISC-V — 64-битный. Фиксация заставила бы эмуляцию. - Обратная совместимость: код, написанный для 16-битных систем, всё ещё может компилироваться.
Практический пример: перенос кода с Windows на Linux
Допустим, у вас есть функция, использующая long для хранения времени:
// Проблемный код
long getTimestamp() { return time(nullptr); }
На Windows long = 4 байта (переполнение в 2038 году!). На Linux — 8 байт. Исправление:
#include <chrono>
int64_t getTimestamp() {
return std::chrono::system_clock::now()
.time_since_epoch().count();
}
Инструменты для проверки
Используйте макросы из <limits>:
#include <limits>
std::cout << "int max: " << std::numeric_limits<int>::max() << "\n";
std::cout << "long max: " << std::numeric_limits<long>::max() << "\n";
В итоге: не бойтесь различий, просто используйте int32_t/int64_t когда нужна точность, и int для "достаточно хорошего" размера. Современные компиляторы оптимизируют эти типы одинаково хорошо.