В стандарте ISO/ANSI C типы int, signed int, и unsigned int могут использоваться как базовый тип для целых битовых полей (integer bitfields).
Реализация битовых полей, заданных через int та же самая, как и для signed int или unsigned int. В компиляторе IAR C/C++ для ARM, битовые поля, заданные через int, интерпретируются как заданные через unsigned int. Кроме того, любой тип целого числа может быть использован в качестве основного типа, когда разрешены расширения языка. Битовые поля в выражениях будут иметь тот же тип данных, что и базовый целый тип.
Компилятор размещает битовые поля в носителе, базируясь на используемом порядке байт (имеется в виду режим little-endian или big-endian). Если по умолчанию работает порядок байт little-endian, и компилятор размещает поля бит от младшего значащего бита (least significant) до старшего (most significant) в байтах переменной контейнера. Если по умолчанию режим big-endian, то компилятор раскладывает битовые поля в контейнере от старших бит до младших. Битовое поле помещается назначенное на последнее доступное место в контейнере, так чтобы все поле уместилось в этот контейнер целиком. Это означает, что контейнер битового поля может накладываться на другие поля структуры, пока порядок полей в структуре сохраняется. Например, в режиме big-endian имеется структура:
struct example{
char a;
short b : 10;
int c : 6;};
Здесь первая декларация создаст unsigned char, который разместится в битах от 24 до 31. Вторая декларация создаст поле signed short integer размером 10 бит. Это поле займет биты от 15 до 6 включительно, поскольку он не поместится в оставшиеся 8 бит первого контейнера short integer. Последнее поле будет размещено в биты от 0 до 5. Получится 32-битное значение, и структура будет размещена в памяти следующим образом:
Размещение битовых полей структуры в памяти, режим big-endian.
Используйте директиву #pragma bitfields=disjoint_types, чтобы принудительно разъединить контейнеры битовых полей, или другими словами, сделать так, чтобы контейнеры не перекрывались. В этом случае структура вышеуказанного примера будет выглядеть так:
Размещение битовых полей структуры в памяти, режим big-endian, применена директива disjoint_types.
Используйте директиву #pragma bitfields=reversed_disjoint_types для размещения битовых полей от наименее значащего бита (least significant bit) к наиболее значащему (most significant bit) в не перекрывающихся контейнерах.
[Сравнение битовых полей и битовых масок]
Раньше я очень увлекался использованием масок для управления отдельными битовыми флажками, считая это довольно эффективным методом обработки двоичных значений. Упрощенный пример (подключение заголовков определения типов bool, u8, u8 для простоты не показано):
typedef struct _TExampleBitfields
{
u16 val16bit;
u8 bits;
}TExampleBitfields;
#define BIT0MASK (1 << 0)
#define BIT1MASK (1 << 1)
#define BIT2MASK (1 << 2)
#define BIT3MASK (1 << 3)
#define BIT4MASK (1 << 4)
#define BIT5MASK (1 << 5)
TExampleBitfields example;void main (void)
{
u8 cnt;
while (1)
{
example.bits = cnt & (1 << 0)? example.bits | BIT0MASK: example.bits & ~BIT0MASK;
example.bits = cnt & (1 << 1)? example.bits | BIT1MASK: example.bits & ~BIT1MASK;
example.bits = cnt & (1 << 2)? example.bits | BIT2MASK: example.bits & ~BIT2MASK;
example.bits = cnt & (1 << 3)? example.bits | BIT3MASK: example.bits & ~BIT3MASK;
example.bits = cnt & (1 << 4)? example.bits | BIT4MASK: example.bits & ~BIT4MASK;
if (example.bits & BIT4MASK)
example.bits = cnt & (1 << 4)? example.bits | BIT5MASK: example.bits & ~BIT5MASK;
cnt++;
}
}
Это неплохо работает, но оказалось, что в таких случаях эффективнее использовать битовые поля. Сравните с другим примером, который делает то же самое:
typedef struct _TExampleBitfields
{
u16 val16bit;
bool bit0: 1;
bool bit1: 1;
bool bit2: 1;
bool bit3: 1;
bool bit4: 1;
bool bit5: 1;
}TExampleBitfields; TExampleBitfields example;
void main (void)
{
u8 cnt;
while (1)
{
example.bit0 = cnt & (1 << 0)? true : false;
example.bit1 = cnt & (1 << 1)? true : false;
example.bit2 = cnt & (1 << 2)? true : false;
example.bit3 = cnt & (1 << 3)? true : false;
example.bit4 = cnt & (1 << 4)? true : false;
if (example.bit4)
example.bit5 = !example.bit5;
cnt++;
}
}
Очевидно, что код получается компактнее и проще для понимания. Однако это еще не все - проверка показала, что размер кода с битовыми полями иногда получается даже меньше, чем кода с использованием масок. |