Подтвердить что ты не робот

Выравнивание памяти в 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.

Может кто-нибудь объяснить, в чем разница?

4b9b3361

Ответ 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. Конечно, все это зависит от используемого компилятора и от того, какие настройки он использует по очереди.