4.3 – Размеры объектов и оператор sizeof

Добавлено 26 апреля 2021 в 21:11

Размеры объектов

Как вы узнали из урока «4.1 – Введение в основные типы данных», память на современных машинах обычно организована в блоки размером с байты, причем каждый байт памяти имеет уникальный адрес. До этого момента было полезно думать о памяти как о связке почтовых ящиков, куда мы можем помещать и извлекать информацию, а переменные в этой аналогии – имена для доступа к этим почтовым ящикам.

Однако эта аналогия не совсем верна в одном отношении – большинство объектов на самом деле занимают более 1 байта памяти. Один объект может использовать 2, 4, 8 или более последовательных адресов памяти. Объем памяти, который использует объект, зависит от его типа данных.

Поскольку мы обычно обращаемся к памяти через имена переменных (а не напрямую через адреса памяти), компилятор может скрыть от нас детали того, сколько байтов использует какой-либо конкретный объект. Когда мы обращаемся к некоторой переменной x, компилятор знает, сколько байтов данных нужно получить (в зависимости от типа переменной x), и может справиться с этой задачей за нас.

Тем не менее, есть несколько причин, по которым полезно знать, сколько памяти использует какой-либо объект.

Во-первых, чем больше памяти использует объект, тем больше информации он может вместить.

Один бит может содержать 2 возможных значения, 0 или 1:

бит 0
0
1

2 бита могут содержать 4 возможных значения:

бит 0бит 1
00
01
10
11

3 бита могут содержать 8 возможных значений:

бит 0бит 1бит 2
000
001
010
011
100
101
110
111

В общем, объект из n битов (где n – целое число) может содержать 2n (2 в степени n, также иногда записывается 2^n) уникальных значений. Следовательно, при байте из 8 битов, объект размером 1 байт может принимать 28 (256) различных значений. Объект, который использует 2 байта, может принимать 2^16 (65536) разных значений!

Таким образом, размер объекта ограничивает количество уникальных значений, которые он может принимать – объекты, которые используют больше байтов, могут принимать большее количество уникальных значений. Мы рассмотрим это дальше, когда поговорим подробнее о целых числах.

Во-вторых, у компьютеров объем свободной памяти ограничен. Каждый раз, когда мы определяем объект, небольшая часть этой свободной памяти используется, пока существует объект. Поскольку у современных компьютеров много памяти, это влияние обычно незначительно. Однако для программ, которым требуется большое количество объектов или данных (например, игра, которая отображает миллионы полигонов), разница между использованием 1- и 8-байтовых объектов может быть значительной.

Ключевой момент


Начинающие программисты часто слишком много внимания уделяют оптимизации своего кода, чтобы использовать как можно меньше памяти. В большинстве случаев достигаемая разница незначительна. Сосредоточьтесь на написании поддерживаемого кода и оптимизируйте его только тогда и там, где выгода будет существенной.

Размеры основных типов данных

Следующий очевидный вопрос – «сколько памяти занимают переменные разных типов данных?». Вы можете быть удивлены, обнаружив, что размер конкретного типа данных зависит от компилятора и/или архитектуры компьютера!

C++ гарантирует только минимальный размер каждого базового типа данных:

КатегорияТипМинимальный размерПримечание
логическийbool1 байт 
символchar1 байтвсегда точно 1 байт
 wchar_t1 байт 
 char16_t2 байтатип C++11
 char32_t4 байтатип C++11
целочисленное значениеshort2 байта 
 int2 байта 
 long4 байта 
 long long8 байттип C99/C++11
с плавающей запятойfloat4 байта 
 double8 байт 
 long double8 байт 

Однако фактический размер переменных на вашем компьютере может быть другим (особенно int, который чаще всего составляет 4 байта).

Лучшая практика


Для максимальной совместимости не следует предполагать, что переменные могут быть больше указанного минимального размера.

Объекты базовых типов данных обычно работают очень быстро.

Оператор sizeof

Чтобы определить размеры типов данных на конкретной машине, C++ предоставляет оператор с именем sizeof. Оператор sizeof – это унарный оператор, который принимает тип или переменную и возвращает ее размер в байтах. Вы можете скомпилировать и запустить следующую программу, чтобы узнать размеры некоторых из ваших типов данных:

#include <iostream>
 
int main()
{
    std::cout << "bool:\t\t" << sizeof(bool) << " bytes\n";
    std::cout << "char:\t\t" << sizeof(char) << " bytes\n";
    std::cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes\n";
    std::cout << "char16_t:\t" << sizeof(char16_t) << " bytes\n"; // только C++11
    std::cout << "char32_t:\t" << sizeof(char32_t) << " bytes\n"; // только C++11
    std::cout << "short:\t\t" << sizeof(short) << " bytes\n";
    std::cout << "int:\t\t" << sizeof(int) << " bytes\n";
    std::cout << "long:\t\t" << sizeof(long) << " bytes\n";
    std::cout << "long long:\t" << sizeof(long long) << " bytes\n"; // только C++11
    std::cout << "float:\t\t" << sizeof(float) << " bytes\n";
    std::cout << "double:\t\t" << sizeof(double) << " bytes\n";
    std::cout << "long double:\t" << sizeof(long double) << " bytes\n";
 
    return 0;
}

Вот результат работы этой программы, полученный автором, на машине x64 при использовании Visual Studio:

bool:           1 bytes
char:           1 bytes
wchar_t:        2 bytes
char16_t:       2 bytes
char32_t:       4 bytes
short:          2 bytes
int:            4 bytes
long:           4 bytes
long long:      8 bytes
float:          4 bytes
double:         8 bytes
long double:    8 bytes

Ваши результаты могут отличаться, если вы используете другой тип машины или другой компилятор. Обратите внимание, что вы не можете использовать оператор sizeof для типа void, поскольку он не имеет размера (это приведет к ошибке компиляции).

Для продвинутых читателей


Если вам интересно, что такое "\t" в приведенной выше программе, это специальный символ, который вставляет табуляцию (в этом примере мы используем ее для выравнивания выходных столбцов). Мы рассмотрим "\t" и другие специальные символы в уроке «4.11 – Символы».

Вы также можете использовать оператор sizeof для имени переменной:

#include <iostream>
 
int main()
{
    int x{};
    std::cout << "x is " << sizeof(x) << " bytes\n";
 
    return 0;
}
x is 4 bytes

Производительность при использовании базовых типов данных

На современных машинах объекты базовых типов данных работают быстро, поэтому производительность при использовании этих типов обычно не должна быть проблемой.

В качестве отступления...


Вы можете предположить, что типы, которые используют меньше памяти, будут быстрее, чем типы, которые используют больше памяти. Это не всегда так. Процессоры часто оптимизированы для обработки данных определенного размера (например, 32 бита), и типы, соответствующие этому размеру, могут обрабатываться быстрее. На такой машине 32-битный int может быть быстрее, чем 16-битный short или 8-битный char.

Теги

C++ / CppsizeofДля начинающихОбучениеПрограммированиеТипы данных

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.