Почему #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