Почему #pragma pack(1) Рассматривает 6-битный член структуры как 8-битный?

Я застрял о #pragma pack(1) неправильное поведение при определении 6-bit поле и предполагает, что это 8-bit, Я прочитал этот вопрос, чтобы решить мою проблему, но это не помогает мне вообще.

В Visual Studio 2012 я определил ниже struct для сохранения Base64 персонажи:

#pragma pack(1)
struct BASE64 {
    CHAR    cChar1 : 6;
    CHAR    cChar2 : 6;
    CHAR    cChar3 : 6;
    CHAR    cChar4 : 6;
};

Теперь я получил его размер с sizeof, но результат не тот, который я ожидал:

printf("%d", sizeof(BASE64));      // should print 3

Результат: 4

Я ожидал, что получить 3 (так как 6 * 4 = 24, так 24 немного 3 байт)

Событие, с которым я проверил 1-bit вместо поля и получил правильный размер (1 байт):

#pragma pack(1)
struct BASE64 {
    CHAR    cChar1 : 2;
    CHAR    cChar2 : 2;
    CHAR    cChar3 : 2;
    CHAR    cChar4 : 2;
};

Собственно, почему 6-bit предполагает 8-bit с #pragma pack(1)?

3 ответа

Решение

#pragma pack обычно упаковывается на байтовых границах, а не на битовых границах. Это предотвращает вставку байтов заполнения между полями, которые вы хотите сохранить сжатыми. Из документации Microsoft (так как вы предоставили winapi и с моим акцентом)

n (необязательно): указывает значение в байтах, которое будет использоваться для упаковки.

То, как реализация обрабатывает битовые поля, когда вы пытаетесь заставить их пересекать границу байта, определяется реализацией. Из стандарта С11 (секция 6.7.2.1 Structure and union specifiers /11опять мой акцент)

Реализация может выделить любой адресуемый блок памяти, достаточно большой для хранения битового поля. Если остается достаточно места, битовое поле, которое непосредственно следует за другим битовым полем в структуре, должно быть упаковано в смежные биты той же единицы. Если остается недостаточно места, определяется, будет ли битовое поле, которое не помещается, в следующий блок или перекрывает соседние блоки, определяется реализацией. Порядок распределения битовых полей внутри блока (от старшего к младшему или от младшего к старшему) определяется реализацией. Выравнивание адресуемой единицы хранения не определено.

В большей части документации MS упоминается это специфическое поведение:

Смежные битовые поля упаковываются в одну и ту же 1-, 2- или 4-байтовую единицу выделения, если целочисленные типы имеют одинаковый размер и если следующее битовое поле помещается в текущую единицу выделения, не пересекая границу, наложенную общими требованиями выравнивания битовых полей.

Простой ответ: это НЕ неправильное поведение.

Упаковка пытается поместить отдельные блоки данных в байты, но не может упаковать два 6-битных блока в один 8-битный байт. Таким образом, компилятор помещает их в отдельные байты, возможно потому, что доступ к одному байту для извлечения или хранения ваших 6-битных данных проще, чем доступ к двум последовательным байтам и обработка некоторой завершающей части одного байта и некоторой ведущей части из другого.

Это определяется реализацией, и вы мало что можете с этим поделать. Вероятно, у оптимизатора есть возможность предпочесть размер скорости - возможно, вы можете использовать его для достижения ожидаемого результата, но я сомневаюсь, что оптимизатор зашел бы так далеко. В любом случае оптимизация размера обычно сокращает код, а не данные (насколько я знаю, но я не эксперт, и я вполне могу ошибаться здесь).

В некоторых реализациях битовые поля не могут охватывать границы переменных. Вы можете определить несколько битовых полей в переменной только в том случае, если их общее число битов соответствует типу данных этой переменной.

В вашем первом примере недостаточно доступных битов в CHAR держать оба cChar1 а также cChar2 когда они 6 бит каждый, так cChar2 должен идти в следующем CHAR в памяти. То же самое с cChar3 а также cChar4, Таким образом, почему общий размер BASE64 составляет 4 байта, а не 3 байта:

  (6 bits + 2 bits padding) = 8 bits
+ (6 bits + 2 bits padding) = 8 bits
+ (6 bits + 2 bits padding) = 8 bits
+ 6 bits
- - - - - - - - - - 
= 30 bits
= needs 4 bytes

Во втором примере достаточно доступных битов в CHAR держать все cChar1...cChar4 когда они 1 бит каждый. Таким образом, почему общий размер BASE64 1 байт, а не 4 байта:

  1 bit
+ 1 bit
+ 1 bit
+ 1 bit
- - - - - - - - - - 
= 4 bits
= needs 1 byte
Другие вопросы по тегам