Как компилятор определяет размер структуры битового поля?

Например:

struct a {
uint32_t    foreColor_ : 32;
uint32_t    backColor_ : 32;
uint16_t    lfHeight_ : 16;
uint16_t    flags_: 4;
bool    lfBold_: 1;
bool    lfItalic_: 1;
bool    lfUnderLine_: 1;
bool    lfDashLine_: 1;
bool    lfStrike_: 1;
bool    lfSubscript_: 1;
bool    lfSuperscript_: 1;
};

16 байтов, но

struct a {
uint32_t    foreColor_ : 32;
uint32_t    backColor_ : 32;
uint16_t    lfHeight_ : 16;
uint8_t     flags_: 4;
bool    lfBold_: 1;
bool    lfItalic_: 1;
bool    lfUnderLine_: 1;
bool    lfDashLine_: 1; //for ime
bool    lfStrike_: 1;
bool    lfSubscript_: 1;
bool    lfSuperscript_: 1;
};

длиной 12 байт.

Я думал, что flags_ должен иметь одинаковую длину, но, похоже, нет.

Зачем?

6

Решение

Стандарт (9,6 из рабочий проект) говорит это:

указывает битовое поле; его длина выделяется из имени битового поля
двоеточием. Атрибут битового поля не является частью типа
член класса. Выражение константы должно быть целым
константное выражение со значением, большим или равным нулю.
Выражение константы может быть больше, чем число битов в
представление объекта (
3.9) типа битового поля; в таких случаях дополнительные биты используются в качестве битов заполнения и не участвуют в представлении значения (
3.9) битового поля. Распределение битовых полей внутри объекта класса определяется реализацией. Выравнивание битовых полей
от реализации
. Битовые поля упакованы в несколько адресуемых
единица распределения. [Примечание: битовые поля распределены в некоторых единицах
машины а не на других. Битовые поля назначаются справа налево на
некоторые машины слева направо на других. —Конечная записка]

(мой акцент)

Так что это будет зависеть от вашего компилятора. Похоже, что происходит в вашем случае — и я бы описал как довольно нормальное поведение — это то, что он объединяет только битовые поля одного типа, а затем упаковывает структуру в 4-байтовую границу, поэтому в первом случае мы имеем:

struct a {
uint32_t    foreColor_ : 32; // 4 bytes (total)
uint32_t    backColor_ : 32; // 8 bytes
uint16_t    lfHeight_ : 16;  // 10 bytes
uint16_t    flags_: 4;       // 12 bytes
bool    lfBold_: 1;          // 13 bytes
bool    lfItalic_: 1;
bool    lfUnderLine_: 1;
bool    lfDashLine_: 1;
bool    lfStrike_: 1;
bool    lfSubscript_: 1;
bool    lfSuperscript_: 1;   // still 13 bytes
};

Который затем дополняется до 16 байтов, а во втором мы имеем:

struct a {
uint32_t    foreColor_ : 32;  // 4 bytes (total)
uint32_t    backColor_ : 32;  // 8 bytes
uint16_t    lfHeight_ : 16;   // 10 bytes
uint8_t     flags_: 4;        // 11 bytes
bool    lfBold_: 1;           // 12 bytes
bool    lfItalic_: 1;
bool    lfUnderLine_: 1;
bool    lfDashLine_: 1;
bool    lfStrike_: 1;
bool    lfSubscript_: 1;
bool    lfSuperscript_: 1;    // still 12 bytes
};

Который не нуждается в заполнении и остается в 12 байтах.

6

Другие решения

Это зависит от компилятора, компилятор выполняет различные выравнивания для оптимизации доступа к полям.

Если вы хотите (нужно) положиться на выравнивание. (например, обработка заголовка сети) Вам нужно использовать #pragma push, pop.

Или __ атрибут __ (упакованный) — который является расширением GCC.

struct {
...
} __attribute__(packed)

Это заставит компилятор сжать его.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector