C++
Макет типов объектов
Поиск…
замечания
См. Также Размер интегральных типов .
Типы классов
Под «классом» мы подразумеваем тип, который был определен с использованием ключевого слова class
или struct
(но не enum class
enum struct
или enum class
).
Даже пустой класс по-прежнему занимает как минимум один байт памяти; поэтому он будет состоять из заполнения. Это гарантирует, что если
p
указывает на объект пустого класса, тоp + 1
является отдельным адресом и указывает на отдельный объект. Однако при использовании в качестве базового класса для пустого класса может иметь размер 0. См. Пустую оптимизацию базы .class Empty_1 {}; // sizeof(Empty_1) == 1 class Empty_2 {}; // sizeof(Empty_2) == 1 class Derived : Empty_1 {}; // sizeof(Derived) == 1 class DoubleDerived : Empty_1, Empty_2 {}; // sizeof(DoubleDerived) == 1 class Holder { Empty_1 e; }; // sizeof(Holder) == 1 class DoubleHolder { Empty_1 e1; Empty_2 e2; }; // sizeof(DoubleHolder) == 2 class DerivedHolder : Empty_1 { Empty_1 e; }; // sizeof(DerivedHolder) == 2
Объектное представление типа класса содержит представления объектов базового класса и нестатических типов членов. Поэтому, например, в следующем классе:
struct S { int x; char* y; };
существует последовательная последовательность
sizeof(int)
байтов внутри объектаS
, называемая подобъектом, который содержит значениеx
, и другой подобъект с байтамиsizeof(char*)
который содержит значениеy
. Эти два не могут чередоваться.Если тип класса имеет члены и / или базовые классы с типами
t1, t2,...tN
, размер должен быть не меньшеsizeof(t1) + sizeof(t2) + ... + sizeof(tN)
учетом предыдущих точек , Однако в зависимости от требований к выравниванию членов и базовых классов компилятору может быть принудительно вставлено дополнение между подобъектами или в начале или конце полного объекта.struct AnInt { int i; }; // sizeof(AnInt) == sizeof(int) // Assuming a typical 32- or 64-bit system, sizeof(AnInt) == 4 (4). struct TwoInts { int i, j; }; // sizeof(TwoInts) >= 2 * sizeof(int) // Assuming a typical 32- or 64-bit system, sizeof(TwoInts) == 8 (4 + 4). struct IntAndChar { int i; char c; }; // sizeof(IntAndChar) >= sizeof(int) + sizeof(char) // Assuming a typical 32- or 64-bit system, sizeof(IntAndChar) == 8 (4 + 1 + padding). struct AnIntDerived : AnInt { long long l; }; // sizeof(AnIntDerived) >= sizeof(AnInt) + sizeof(long long) // Assuming a typical 32- or 64-bit system, sizeof(AnIntDerived) == 16 (4 + padding + 8).
Если заполнение вставляется в объект из-за требований к выравниванию, размер будет больше, чем сумма размеров членов и базовых классов. При выравнивании
n
байт размер обычно будет наименьшим кратнымn
который больше, чем размер всех членов и базовых классов. Каждый членmemN
, как правило, помещается по адресу, который является кратнымalignof(memN)
, иn
обычно будет самым большимalignof
из всехalignof
. Из-за этого, если член с меньшимalignof
сопровождается элементом с большимalignof
, существует вероятность того, что последний элемент не будет правильно выровнен, если он будет установлен сразу после первого. В этом случае прокладка (также известная как элемент выравнивания ) будет помещена между двумя элементами, так что последний элемент может иметь желаемое выравнивание. И наоборот, если для элемента с большимalignof
следует элемент с меньшимalignof
, обычно никакихalignof
не требуется. Этот процесс также известен как «упаковка».
Из-за того, что классы, обычно разделяющиеalignof
их члена с наибольшимalignof
, классы обычно выравниваются сalignof
самого большого встроенного типа, который они прямо или косвенно содержат.// Assume sizeof(short) == 2, sizeof(int) == 4, and sizeof(long long) == 8. // Assume 4-byte alignment is specified to the compiler. struct Char { char c; }; // sizeof(Char) == 1 (sizeof(char)) struct Int { int i; }; // sizeof(Int) == 4 (sizeof(int)) struct CharInt { char c; int i; }; // sizeof(CharInt) == 8 (1 (char) + 3 (padding) + 4 (int)) struct ShortIntCharInt { short s; int i; char c; int j; }; // sizeof(ShortIntCharInt) == 16 (2 (short) + 2 (padding) + 4 (int) + 1 (char) + // 3 (padding) + 4 (int)) struct ShortIntCharCharInt { short s; int i; char c; char d; int j; }; // sizeof(ShortIntCharCharInt) == 16 (2 (short) + 2 (padding) + 4 (int) + 1 (char) + // 1 (char) + 2 (padding) + 4 (int)) struct ShortCharShortInt { short s; char c; short t; int i; }; // sizeof(ShortCharShortInt) == 12 (2 (short) + 1 (char) + 1 (padding) + 2 (short) + // 2 (padding) + 4 (int)) struct IntLLInt { int i; long long l; int j; }; // sizeof(IntLLInt) == 16 (4 (int) + 8 (long long) + 4 (int)) // If packing isn't explicitly specified, most compilers will pack this as // 8-byte alignment, such that: // sizeof(IntLLInt) == 24 (4 (int) + 4 (padding) + 8 (long long) + // 4 (int) + 4 (padding)) // Assume sizeof(bool) == 1, sizeof(ShortIntCharInt) == 16, and sizeof(IntLLInt) == 24. // Assume default alignment: alignof(ShortIntCharInt) == 4, alignof(IntLLInt) == 8. struct ShortChar3ArrShortInt { short s; char c3[3]; short t; int i; }; // ShortChar3ArrShortInt has 4-byte alignment: alignof(int) >= alignof(char) && // alignof(int) >= alignof(short) // sizeof(ShortChar3ArrShortInt) == 12 (2 (short) + 3 (char[3]) + 1 (padding) + // 2 (short) + 4 (int)) // Note that t is placed at alignment of 2, not 4. alignof(short) == 2. struct Large_1 { ShortIntCharInt sici; bool b; ShortIntCharInt tjdj; }; // Large_1 has 4-byte alignment. // alignof(ShortIntCharInt) == alignof(int) == 4 // alignof(b) == 1 // Therefore, alignof(Large_1) == 4. // sizeof(Large_1) == 36 (16 (ShortIntCharInt) + 1 (bool) + 3 (padding) + // 16 (ShortIntCharInt)) struct Large_2 { IntLLInt illi; float f; IntLLInt jmmj; }; // Large_2 has 8-byte alignment. // alignof(IntLLInt) == alignof(long long) == 8 // alignof(float) == 4 // Therefore, alignof(Large_2) == 8. // sizeof(Large_2) == 56 (24 (IntLLInt) + 4 (float) + 4 (padding) + 24 (IntLLInt))
Если строгое выравнивание принудительно
alignas
с помощьюalignas
, тоalignas
будет использоваться, чтобы заставить тип соответствовать заданному выравниванию, даже если он в противном случае был бы меньшим. Например, с приведенным ниже определениемChars<5>
будет иметь три (или, возможно, больше) байта заполнения, вставленные в конец, так что его общий размер равен 8. Невозможно, чтобы класс с выравниванием 4 имел размер из 5, потому что было бы невозможно создать массив этого класса, поэтому размер должен быть «округлен» до кратного 4, вставив прописные байты.// This type shall always be aligned to a multiple of 4. Padding shall be inserted as // needed. // Chars<1>..Chars<4> are 4 bytes, Chars<5>..Chars<8> are 8 bytes, etc. template<size_t SZ> struct alignas(4) Chars { char arr[SZ]; }; static_assert(sizeof(Chars<1>) == sizeof(Chars<4>), "Alignment is strict.\n");
- Если два нестатических члена класса имеют один и тот же спецификатор доступа , то тот, который приходит позже в порядке объявления, гарантированно будет представлен позже в представлении объекта. Но если два нестатических члена имеют разные спецификаторы доступа, их относительный порядок внутри объекта не указан.
- Не определено, какой порядок субобъекты базового класса появляются внутри объекта, независимо от того, происходят ли они последовательно, и появляются ли они до, после или между субобъектами-членами.
Арифметические типы
Узкие типы символов
Тип unsigned char
использует все биты для представления двоичного числа. Поэтому, например, если unsigned char
имеет длину 8 бит, то 256 возможных битовых шаблонов объекта char
представляют 256 различных значений {0, 1, ..., 255}. Число 42 гарантированно будет представлено битовой диаграммой 00101010
.
signed char
тип signed char
не имеет битов заполнения, т. Е. Если signed char
имеет длину 8 бит, тогда он имеет 8 бит емкости для представления числа.
Обратите внимание, что эти гарантии не применяются к типам, отличным от типов узких символов.
Целочисленные типы
Беззнаковые целые типы используют чистую двоичную систему, но могут содержать биты заполнения. Например, для unsigned int
может быть (хотя и маловероятно) иметь длину 64 бита, но только быть способным хранить целые числа от 0 до 2 32 - 1 включительно. Остальные 32 бита будут битами заполнения, которые не должны записываться напрямую.
Подписанные целочисленные типы используют двоичную систему со знаковым битом и, возможно, битами заполнения. Значения, которые относятся к общему диапазону знакового целочисленного типа и соответствующего беззнакового целочисленного типа, имеют одинаковое представление. Например, если битовый шаблон 0001010010101011
unsigned short
объекта представляет значение 5291
, тогда он также представляет значение 5291
при интерпретации как short
объект.
Определяется реализация, используется ли дополнение двух дополнений, дополнений или знаковых значений, поскольку все три системы удовлетворяют требованию в предыдущем абзаце.
Типы с плавающей точкой
Представление значений типов с плавающей запятой определяется реализацией. Чаще всего float
и double
типы соответствуют IEEE 754 и имеют длину 32 и 64 бит (так, например, float
будет иметь 23 бита точности, которые будут следовать за 8 экспоненциальными битами и 1 знаковым битом). Однако стандарт ничего не гарантирует. Типы плавающей точки часто имеют «ловушечные представления», которые вызывают ошибки, когда они используются в вычислениях.
Массивы
Тип массива не имеет дополнений между его элементами. Следовательно, массив с типом элемента T
является просто последовательностью объектов T
выложенных в памяти, по порядку.
Многомерный массив представляет собой массив массивов, и приведенное выше относится к рекурсивно. Например, если у нас есть декларация
int a[5][3];
то a
представляет собой массив из 5 массивов из 3 int
s. Поэтому a[0]
, состоящая из трех элементов a[0][0]
, a[0][1]
, a[0][2]
, изложена в памяти до a[1]
, которая состоит из из a[1][0]
, a[1][1]
и a[1][2]
. Это называется строчным порядком.