Доступ к битам в символе в C

У меня есть шестнадцатеричное число 0x37, а его двоичное представление – 0011 0111. Как мне получить доступ к первым двум битам двоичного представления, которое равно «11»? Как использовать бит-сдвиг или маскирование для достижения этого? Я могу получить доступ пополам, но не два бита за один раз?

Если вы & ваш номер с 0x03, вы получите последние два бита.

 char c = 0x37; char mask = 0x03; char lastTwo = c & mask; 

Вот образец для доступа к нему по частям:

 #include  int main() { char byte = 0x37; int i; for(i = 7; 0 <= i; i --) printf("%d\n", (byte >> i) & 0x01); return 0; } 

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

Вот пример, написанный на компьютере Ubuntu Linux и протестированный с помощью GCC.

 #include  #include  #pragma pack(1) typedef struct { unsigned int low2: 2; // 2 bits of the byte unsigned int high6: 6; // 6 more bits of the byte } MYBYTE; typedef union { MYBYTE mybyte; unsigned char b; } MYUNION; main() { MYUNION m; assert(sizeof(m) == 1); mb = 0x03; assert(m.mybyte.low2 == 0x03); assert(m.mybyte.high6 == 0x00); printf("low2 of 0x03 is: %u\n", m.mybyte.low2); printf("high6 of 0x03 is: %u\n", m.mybyte.high6); mb = 0xff; printf("low2 of 0x03 is: %u\n", m.mybyte.low2); printf("high6 of 0x03 is: %u\n", m.mybyte.high6); assert(m.mybyte.low2 == 0x03); assert(m.mybyte.high6 == 0x3f); m.mybyte.high6 = 0x1c; m.mybyte.low2 = 0x01; assert(mb == 0x71); printf("mb is: 0x%02x\n", mb); return 0; } 

Союз существует, поэтому мы можем получить к нему доступ как полный байт или получить доступ к нему по битовым полям. #pragma pack(1) должен убедиться, что бит-бит упал до байта, без лишних битов заполнения. (Как я уже говорил, вы полагаетесь на детали реализации, когда используете битовые поля.)

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

Если вы собираетесь использовать такой код, всегда полезно иметь некоторые утверждения, которые гарантируют, что он работает.

Если вы не собираетесь использовать бит-поля, я предлагаю вам определить функцию, которая выполняет ваши смены и маскировку, чтобы убедиться, что вы не испортили. Может быть, что-то вроде этого:

 #include  static unsigned int _bit_masks[] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, }; #define MIN(a, b) \ ((a) < (b) ? (a) : (b)) unsigned int bits(unsigned int x, unsigned int i_bit, unsigned int c_bits) { assert(UINT_MAX >= 4294967295U); // unsigned int must be at least 32-bit assert(i_bit <= 31); if (i_bit > 31) return 0; c_bits = MIN(c_bits, 32 - i_bit); // shift-and-mask to grab the requested bits, and return those bits return (x >> i_bit) & _bit_masks[c_bits]; } 

Вы передаете значение, затем какую позицию бита вы хотите получить, и сколько бит вы хотите. Таким образом, чтобы захватить 6 бит, начиная с битовой позиции 2, с тестовым значением 0x71 вы могли бы позвонить:

 x = bits(0x71, 2, 6); // x is set to 0x1c 

Если вам не нравится таблица поиска, и вы хотите, чтобы крошечный код сделал это, вы можете использовать:

 unsigned int bits(unsigned int x, unsigned int i_bit, unsigned int c_bits) { const unsigned int mask_bits = 0xffffffff; assert(UINT_MAX >= 4294967295U); // unsigned int must be at least 32-bit assert(i_bit <= 31); if (i_bit > 31) return 0; c_bits = MIN(c_bits, 32 - i_bit); // shift-and-mask to grab the requested bits, and return those bits return (x >> i_bit) & (mask_bits >> (32 - c_bits)); } 

Вы должны убедиться, что биты маски объявлены unsigned потому что если они подписаны, операция смены вправо будет расширяться.

Если вы объявите эту последнюю версию функции как inline, поместите ее в заголовочные файлы и вызовите ее с постоянными значениями для i_bit и c_bits , она скомпилируется до минимального кода для решения проблемы. (Например, если i_bit равно 0, компилятор знает, что >> 0 ничего не делает и просто не сгенерирует этот код. И если компилятор знает c_bits как константу, он может выполнять всю работу смены mask_bits при компиляции время.) Но вам нужно убедиться, что вы используете версию assert() которая не компилируется ни в чем из вашей сборки релиза, или же используйте собственный макрос ASSERT() и не скомпилируйте макрос.

Лучше всего использовать битовую маскировку, как вы уже упоминали. Что-то вроде этого должно сделать трюк:

 x = 0x37; y = x&0x30; //Mask out the first two bits of the higher nibble y = y>>4; 

Это самая простая функция для использования, я не хочу, чтобы другие долгое время боролись, прежде чем получить что-то похожее на это –

 char get_bits(char a, char no_of_bits) { return a & ((no_of_bits << 1) - 1); } char a = 0x37; char b = get_bits(a, 2); 

Надеюсь, это поможет кому-то в будущем