Как должен работать #pragma pack(8)?

Я новичок в структуре выравнивания и упаковки. Я думал, что понял это, но я нахожу некоторые результаты, которые я не ожидал (см. Ниже).

Мое понимание выравнивания структуры:

  1. Типы обычно выровнены по адресам памяти, кратным их размеру.

  2. Обивка добавляется по мере необходимости для облегчения правильного выравнивания

  3. Конец структуры должен быть дополнен до кратного наибольшего элемента (для облегчения доступа к массиву)

#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 байтов.

Другие вопросы по тегам