Как должен работать #pragma pack(8)?
Я новичок в структуре выравнивания и упаковки. Я думал, что понял это, но я нахожу некоторые результаты, которые я не ожидал (см. Ниже).
Мое понимание выравнивания структуры:
Типы обычно выровнены по адресам памяти, кратным их размеру.
Обивка добавляется по мере необходимости для облегчения правильного выравнивания
Конец структуры должен быть дополнен до кратного наибольшего элемента (для облегчения доступа к массиву)
#pragma pack
Директива в основном позволяет переопределить общее соглашение о выравнивании на основе размера типа:
#pragma pack(push, 8)
struct SPack8
{
// Assume short int is 2 bytes, double is 8 bytes, and int is 4 bytes
short int a;
double b;
short int c;
int d;
};
#pragma pack(pop)
Pseudo struct layout: What I expected:
// note: PADDING IS BRACKETED
0, 1, [2, 3, 4, 5, 6, 7] // a occupies address 0, 1
8, 9, 10, 11, 12, 13, 14, 15, // b occupies 8-15 inclusive
16, 17, [18, 19, 20, 21, 22, 23] // c occupies 16-17 inclusive
24, 25, 26, 27 // d occupies 24-27 inclusive
// Thus far, SPack8 is 28 bytes, but the structure must be a multiple of
// sizeof(double) so we need to add padding to make it 32 bytes
[28, 29, 30, 31]
К моему удивлению, sizeof(SPack8) == 24 на VS 2015 x86. Кажется, что d не выравнивается по 8-байтовому адресу:
offsetof(SPack, a) // 0, as expected
offsetof(SPack, b) // 8, as expected
offsetof(Spack, c) // 16, as expected
offsetof(SPack, d) // 20..what??
Может кто-нибудь объяснить, пожалуйста, что происходит / что я неправильно понял?
Спасибо!
1 ответ
Ваше недоразумение в том, что #pragma pack
позволяет расширить структуру, это не так. pack
позволяет вам упаковать структуру более плотно при необходимости. #pragma pack(push, 8)
говорит компилятору, что он может максимально выровнять по 8-байтовой границе, но не более.
Пример:
#pragma pack(push, 2)
struct X {
char a; // 1 byte
// 1 byte padding
int b; // 4 bytes, note though that it's aligned on 2 bytes, not 4.
char c, d, e; // 3 bytes
//1 byte padding
}; // == 10 bytes, the whole struct is also aligned on 2 bytes, not 4
#pragma pack(pop)
// The same struct without the pragma pack:
struct Y {
char a; // 1 byte
// 3 bytes padding
int b; // 4 bytes
char c, d, e; // 3 bytes
// 1 byte padding
};
Это то, что pack
делает, используя меньше отступов, как обычно использует компилятор. В вашем примере вы пытались выровнять int
на 8-байтовой границе, но так как вы позволили компилятору выравнивать не более 8 байтов, 4-байтовое выравнивание, которое компилятор хотел бы использовать, прекрасно. Вся ваша структура, которая имеет размер 24, также имеет размер, кратный 8 (ваш самый большой член), поэтому для ее заполнения не требуется заполнение до 32.
Вы можете принудительно выровнять свою структуру
__declspec(align(32)) struct Z {
char a;
int b;
char c, d, e;
};
или даже член вашей структуры
struct SPack8
{
// Assume short int is 2 bytes, double is 8 bytes, and int is 4 bytes
short int a;
double b;
short int c;
__declspec(align(8)) int d;
};
на определенной границе, но я не вижу причин для принудительного выравнивания 4-байтового типа на 8 байтов.