Avatar ·

Что стандарт C++ говорит о размере типов int, long и long long?

Всем привет! Я только начал изучать C++ и столкнулся с путаницей. Я знаю, что в разных компиляторах и на разных платформах размеры целочисленных типов могут отличаться. Я написал простой код, чтобы проверить это на своей машине (Windows, MinGW): ```cpp #include using namespace std; int main() { cout << "Size of int: " << sizeof(int) << " bytes" << endl; cout << "Size of long: " << sizeof(long) << " bytes" << endl; cout << "Size of long long: " << sizeof(long long) << " bytes" << endl; return 0; } ``` На моём компьютере я получил: int = 4 байта, long = 4 байта, long long = 8 байт. Но в интернете я читал, что на Linux x86_64 long может быть 8 байт. Мой друг сказал, что на старых 16-битных системах int был 2 байта. Меня интересует: что на самом деле гарантирует стандарт C++? Могу ли я положиться на то, что int всегда будет 4 байта? Почему long и int одинаковы на моей Windows, но разные на Linux? И как писать переносимый код, если размеры плавают? Заранее спасибо за разъяснения! <<>>

Гарантии стандарта 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³¹-1
    • LLONG_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 бита. Вот почему на вашей Windows long = 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");

Практические рекомендации

  1. Для циклов и индексов: используйте int или size_t — это оптимальный размер для платформы.
  2. Для хранения больших чисел: int64_t или long long.
  3. Для совместимости с API: используйте типы, которые ожидает API (например, long в WinAPI).
  4. Избегайте 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 для "достаточно хорошего" размера. Современные компиляторы оптимизируют эти типы одинаково хорошо.