Выравнивание памяти в C-структурах
Я работаю над 32-разрядной машиной, поэтому я полагаю, что выравнивание памяти должно быть 4 байта. Скажем, у меня есть структура:
typedef struct {
unsigned short v1;
unsigned short v2;
unsigned short v3;
} myStruct;
реальный размер составляет 6 байтов, и я полагаю, что выровненный размер должен быть 8, но sizeof(myStruct)
возвращает мне 6.
Однако, если я пишу:
typedef struct {
unsigned short v1;
unsigned short v2;
unsigned short v3;
int i;
} myStruct;
реальный размер - 10 байт, выровненный - 12, и на этот раз sizeof(myStruct) == 12
.
Может кто-нибудь объяснить, в чем разница?
Ответы
Ответ 1
По крайней мере, на большинстве машин тип только выравнивается с границей, такой же большой, как и сам тип [Edit: вы не можете действительно требовать "более" выравнивания, чем это, потому что вы должны иметь возможность создавать массивы, и вы не можете вставить дополнение в массив]. В вашей реализации short
, по-видимому, 2 байта и int
4 байта.
Это означает, что ваша первая структура выровнена с 2-байтной границей. Поскольку все члены имеют 2 байта за штуку, между ними не добавляется дополнение.
Второй содержит 4-байтовый элемент, который выравнивается с 4-байтной границей. Поскольку ему предшествуют 6 байтов, между v3
и i
вставлено 2 байта заполнения, дающее 6 байтов данных в short
s, два байта заполнения и еще 4 байта данных в int
в общей сложности 12.
Ответ 2
Забудьте о разных членах, даже если вы пишете две структуры, члены которых точно такие же, с разницей является то, что порядок, в котором они объявлены, отличается, тогда размер каждой структуры может быть (и часто) разными.
Например, см. это,
#include <iostream>
using namespace std;
struct A
{
char c;
char d;
int i;
};
struct B
{
char c;
int i; //note the order is different!
char d;
};
int main() {
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
}
Скомпилируйте его с помощью gcc-4.3.4
, и вы получите этот вывод:
8
12
То есть, размеры отличаются друг от друга, хотя обе структуры имеют одинаковые элементы!
Код в Ideone: http://ideone.com/HGGVl
Суть в том, что в стандарте не говорится о том, как следует выполнять отладку, и поэтому компиляторы могут принимать какие-либо решения, а вы не можете предполагать, что все компиляторы принимают одно и то же решение.
Ответ 3
По умолчанию значения выравниваются в соответствии с их размером. Таким образом, 2-байтовое значение, подобное a short
, выровнено по 2-байтовой границе, а 4-байтовое значение, подобное int
, выровнено по 4-байтовой границе
В вашем примере перед i
добавляются 2 байта заполнения, чтобы гарантировать, что i
падает на 4-байтовую границу.
(Вся структура выровнена на границе, по крайней мере, такой же большой, как и наибольшее значение в структуре, поэтому ваша структура будет выровнена с 4-байтной границей.)
Фактические правила варьируются в зависимости от платформы - страница Wikipedia на Уравнивание структуры данных содержит более подробную информацию.
Компиляторы обычно позволяют управлять упаковкой через (например) директивы #pragma pack
.
Ответ 4
Во-первых, в то время как спецификация заполнения дополняется компилятором, ОС также накладывает некоторые правила в отношении требований к выравниванию. Этот ответ предполагает, что вы используете gcc, хотя ОС может меняться
Чтобы определить пространство, занимаемое данной структурой и ее элементами, вы можете следовать этим правилам:
Сначала предположим, что структура всегда начинается с адреса, который правильно выровнен для всех типов данных.
Затем для каждой записи в структуре:
- Минимальное требуемое пространство - это необработанный размер элемента, заданный
sizeof(element)
.
- Требование к выравниванию элемента - это требование выравнивания базового типа элемента.
Примечательно, что это означает, что требование выравнивания для массива
char[20]
такое же, как
требование для простой char
.
Наконец, требование выравнивания структуры в целом является максимальным значением требований к выравниванию для каждого из его элементов.
gcc будет вставлять заполнение после заданного элемента, чтобы убедиться, что следующая (или структура, если мы говорим о последнем элементе) правильно выровнены. Он никогда не будет изменять порядок элементов в структуре, даже если это будет экономить память.
Теперь сами требования к выравниванию также немного нечетны.
- 32-разрядный Linux требует, чтобы 2-байтные типы данных имели 2-байтовое выравнивание (их адреса должны быть четными). Все более крупные типы данных должны иметь 4-байтовое выравнивание (адреса заканчиваются на
0x0
, 0x4
, 0x8
или 0xC
). Обратите внимание, что это относится и к типам, размер которых превышает 4 байта (например, double
и long double
).
- 32-разрядная версия Windows более строгая, если тип K-байтов по размеру, он должен быть выровнен по байтам. Это означает, что a
double
может помещаться только по адресу, заканчивающемуся на 0x0
или 0x8
. Единственным исключением из этого является long double
, который по-прежнему выравнивается по 4 байт, даже если он на самом деле составляет 12 байтов.
- Для Linux и Windows на 64-битных машинах тип байтов K должен быть выровнен по байтам. Опять же,
long double
является исключением и должен быть выровнен по 16 байт.
Ответ 5
Предполагая, что:
sizeof(unsigned short) == 2
sizeof(int) == 4
Затем я лично использовал бы следующее (ваш компилятор может отличаться):
unsigned shorts are aligned to 2 byte boundaries
int will be aligned to 4 byte boundaries.
typedef struct
{
unsigned short v1; // 0 bytes offset
unsigned short v2; // 2 bytes offset
unsigned short v3; // 4 bytes offset
} myStruct; // End 6 bytes.
// No part is required to align tighter than 2 bytes.
// So whole structure can be 2 byte aligned.
typedef struct
{
unsigned short v1; // 0 bytes offset
unsigned short v2; // 2 bytes offset
unsigned short v3; // 4 bytes offset
/// Padding // 6-7 padding (so i is 4 byte aligned
int i; // 8 bytes offset
} myStruct; // End 12 bytes
// Whole structure needs to be 4 byte aligned.
// So that i is correctly aligned.
Ответ 6
Каждый тип данных должен быть выровнен на границе памяти собственного размера. Таким образом, short
должен быть выровнен по 2-байтовой границе, а int
должен быть на 4-байтовой границе. Аналогично, a long long
должен был бы находиться на 8-байтовой границе.
Ответ 7
В вашей первой структуре, поскольку каждый элемент имеет размер short
, вся структура может быть выровнена на границах short
, поэтому в конце не нужно добавлять какие-либо отступы.
Во второй структуре int (предположительно 32 бита) должен быть выровнен по словам, поэтому он вставляет отступы между v3
и i
для выравнивания i
.
Ответ 8
Причиной второго sizeof(myStruct)
является 12
является заполнение, которое вставлено между v3
и i
, чтобы выровнять i
с 32-разрядной границей. Существует два байта.
Wikipedia объясняет прокладку и выравнивание достаточно четко.
Ответ 9
В стандарте не сказано много о структуре структур с полными типами - это до компилятора. Он решил, что ему нужно, чтобы int начинал с границы для доступа к нему, но поскольку он должен выполнять адресацию подграничной памяти для шорт, нет необходимости вставлять их
Ответ 10
Похоже, что он привязан к bounderies на основе размера каждого var, так что адрес кратен размеру, к которому обращаются (так что шорты выровнены с 2, ints, выровненными до 4 и т.д.), если вы переместили один из шорты после int, sizeof(mystruct)
должны быть 10. Конечно, все это зависит от используемого компилятора и от того, какие настройки он использует по очереди.