Несмотря на то, что стандарт ANSI C слишком мало определяет, как упаковываются битовые поля, чтобы предложить какое-либо существенное преимущество перед «компиляторам разрешено упаковывать битовые поля, как они считают нужным», тем не менее во многих случаях он запрещает компиляторам упаковывать вещи наиболее эффективным способом.
В частности, если структура содержит битовые поля, компилятор должен сохранить ее как структуру, которая содержит одно или несколько анонимных полей некоторого «нормального» типа хранения, а затем логически разделить каждое такое поле на составляющие его части битового поля. Таким образом, учитывая:
unsigned char foo1: 3;
unsigned char foo2: 3;
unsigned char foo3: 3;
unsigned char foo4: 3;
unsigned char foo5: 3;
unsigned char foo6: 3;
unsigned char foo7: 3;
Если unsigned char
это 8 бит, компилятор должен будет выделить четыре поля этого типа и назначить два битовых поля всем, кроме одного (которое будет в отдельном char
поле). Если бы все char
объявления были заменены на short
, то было бы два поля типа short
, одно из которых могло бы содержать пять битовых полей, а другое - оставшиеся два.
На процессоре без ограничений по выравниванию данные могут быть размещены более эффективно, используя unsigned short
для первых пяти полей и unsigned char
последних двух, сохраняя семь трехбитовых полей в трех байтах. Хотя должна быть возможность хранить восемь трехбитных полей в трех байтах, компилятор мог разрешить это только при наличии трехбайтового числового типа, который можно было бы использовать как тип «внешнего поля».
Лично я считаю, что битовые поля в основном бесполезны. Если код должен работать с двоично упакованными данными, он должен явно определить места хранения фактических типов, а затем использовать макросы или другие подобные средства для доступа к их битам. Было бы полезно, если бы C поддерживал такой синтаксис, как:
unsigned short f1;
unsigned char f2;
union foo1 = f1:0.3;
union foo2 = f1:3.3;
union foo3 = f1:6.3;
union foo4 = f1:9.3;
union foo5 = f1:12.3;
union foo6 = f2:0.3;
union foo7 = f2:3.3;
Такой синтаксис, если он разрешен, позволил бы коду использовать битовые поля переносимым образом, без учета размеров слов или порядка байтов (foo0 будет в трех младших битах f1, но они могут храниться в нижний или верхний адрес). Однако при отсутствии такой функции макросы, вероятно, являются единственным переносимым способом работы с такими вещами.