Сколько байт в int и long в C++? Стандарт отвечает неожиданно

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Загадка размеров: почему int и long ведут себя по-разному на разных компиляторах?

Каждый, кто серьёзно изучал C++, рано или поздно сталкивался с этой головоломкой. Вы пишете простой код:

#include <iostream>

int main() {
    std::cout << "Размер int: " << sizeof(int) << " байт\n";
    std::cout << "Размер long: " << sizeof(long) << " байт\n";
    std::cout << "Размер long long: " << sizeof(long long) << " байт\n";
    return 0;
}

Запускаете на своём ноутбуке — получаете 4, 4, 8. Запускаете на сервере Linux — 4, 8, 8. А на старой 16-битной системе (да, такие ещё живы!) — 2, 4, 8. В чём подвох? Почему C++, язык, славящийся своей мощью и контролем, допускает такую неопределённость?

Ответ прост и сложен одновременно: стандарт C++ сознательно оставляет размеры целочисленных типов на усмотрение реализации. Это не баг, а фича, которая позволяет языку работать на любом железе — от микроконтроллеров до суперкомпьютеров. Но для разработчика это означает одно: если вы пишете переносимый код, нельзя полагаться на конкретные размеры.

Давайте разберёмся, что же на самом деле гарантирует стандарт C++ (начиная с C++98 и до C++23) и как не попасть в ловушку платформозависимости.

Что гарантирует стандарт C++? Минимальные размеры и иерархия

Вместо того чтобы сказать "int — это 4 байта", стандарт устанавливает минимальные диапазоны значений и строгую иерархию размеров. Вот ключевые правила из черновика стандарта (раздел [basic.fundamental]):

  • Минимальный размер char: 1 байт (8 бит, хотя формально байт в C++ может быть и больше).
  • Иерархия размеров: sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long) ≤ sizeof(long long).
  • Минимальные биты:
    • short: не менее 16 бит.
    • int: не менее 16 бит.
    • long: не менее 32 бит.
    • long long: не менее 64 бит (добавлен в C++11).
  • Размер int: стандарт рекомендует, чтобы int имел "естественный размер" для целевой архитектуры (обычно 4 байта на 32/64-битных системах).

Обратите внимание: long может быть равен int (на 32-битных Windows) или long long (на 64-битных Linux). Это абсолютно легально!

Практический пример: проверка на вашей системе

Запустите этот код и сравните результаты с коллегами из разных ОС:

#include <iostream>
#include <climits> // для INT_MAX, LONG_MAX и т.д.

int main() {
    std::cout << "Размеры в байтах:\n";
    std::cout << "short: " << sizeof(short) << "\n";
    std::cout << "int: " << sizeof(int) << "\n";
    std::cout << "long: " << sizeof(long) << "\n";
    std::cout << "long long: " << sizeof(long long) << "\n\n";

    std::cout << "Максимальные значения:\n";
    std::cout << "INT_MAX: " << INT_MAX << "\n";
    std::cout << "LONG_MAX: " << LONG_MAX << "\n";
    std::cout << "LLONG_MAX: " << LLONG_MAX << "\n";
    return 0;
}

Вы увидите, что на Windows (MSVC) long часто равен 4 байтам, а на Linux (GCC/Clang) — 8 байтам. Это классический источник багов при переносе кода между платформами.

Почему стандарт не фиксирует размеры? Исторический контекст

Представьте себе 1970-е годы. Процессоры были 8-битными, 16-битными, а 32-битные только появлялись. Создатели C (а затем и C++) хотели, чтобы язык мог эффективно работать на любом оборудовании. Если бы int был жёстко закреплён как 4 байта, на 8-битных микроконтроллерах каждая операция с int требовала бы нескольких тактов, что убило бы производительность.

Поэтому родилась гениальная идея: int — это "натуральный" размер слова для данной архитектуры. На 16-битном процессоре int — 2 байта, на 32-битном — 4, на 64-битном — обычно 4 (но могло бы быть и 8, если бы не обратная совместимость).

Эта гибкость стала и проклятием: код, работающий на одной платформе, может неожиданно сломаться на другой из-за переполнения или несовпадения размеров.

Как писать переносимый код? Три стратегии

1. Используйте типы фиксированной ширины из <cstdint>

Начиная с C++11, стандартная библиотека предоставляет типы с гарантированным размером:

#include <cstdint>
#include <iostream>

int main() {
    int32_t my_int;    // всегда 4 байта, если доступно
    int64_t my_long;   // всегда 8 байт
    int16_t my_short;  // всегда 2 байта
    
    std::cout << "int32_t: " << sizeof(int32_t) << " байт\n";
    std::cout << "int64_t: " << sizeof(int64_t) << " байт\n";
    return 0;
}

Важно: эти типы опциональны. Они гарантированно есть на всех современных платформах, но могут отсутствовать на очень старых или экзотических (например, 16-битные DSP). Если нужна максимальная переносимость, используйте int_least32_t (как минимум 32 бита) или int_fast32_t (самый быстрый тип, способный хранить 32 бита).

2. Используйте sizeof для проверок и assert'ов

Блоги

Рекомендаций по книгам