побитовое индексирование в C?
-
09-06-2019 - |
Вопрос
Я пытаюсь реализовать идею сжатия данных, которая у меня была, и поскольку я представляю себе ее выполнение с большим объемом тестовых данных, я подумал закодировать ее на C (в основном у меня есть опыт работы со скриптовыми языками, такими как Ruby и Tcl.)
Просматривая книги О'Рейли "корова" по C, я понимаю, что не могу просто проиндексировать биты простой переменной типа 'char' или 'int', как мне хотелось бы, для выполнения побитовых сравнений и операторов.
Прав ли я в своем восприятии?Разумно ли для меня использовать перечислимый тип для представления бита (и создать массив из них, а также написать функции для преобразования в символ и из него)?Если да, то определен ли такой тип и функции уже где-нибудь в стандартной библиотеке?Существуют ли другие (лучшие?) подходы?Есть ли где-нибудь какой-нибудь пример кода, на который кто-нибудь мог бы мне указать?
Спасибо -
Решение
Исходя из того, что сказал Кайл, вы можете использовать макрос, который выполнит всю тяжелую работу за вас.
Это вполне возможно.
Чтобы установить n-й бит, используйте ИЛИ:
x |= (1 << 5);// устанавливает 6-й-от справа
Чтобы немного очистить, используйте И:
x &= ~(1 << 5);// очищается 6-й справа
Чтобы немного перевернуть, используйте XOR:
x ^= (1 << 5);// переворачивает 6-й справа
Или...
#define GetBit(var, bit) ((var & (1 << bit)) != 0) // Returns true / false if bit is set
#define SetBit(var, bit) (var |= (1 << bit))
#define FlipBit(var, bit) (var ^= (1 << bit))
Затем вы можете использовать это в коде типа:
int myVar = 0;
SetBit(myVar, 5);
if (GetBit(myVar, 5))
{
// Do something
}
Другие советы
Это вполне возможно.
Чтобы установить n-й бит, используйте ИЛИ:
x |= (1 << 5); // sets the 5th-from right
Чтобы немного очистить, используйте И:
x &= ~(1 << 5); // clears 5th-from-right
Чтобы немного перевернуть, используйте XOR:
x ^= (1 << 5); // flips 5th-from-right
Чтобы получить значение бита, используйте shift и И:
(x & (1 << 5)) >> 5 // gets the value (0 or 1) of the 5th-from-right
примечание:сдвиг вправо на 5 предназначен для того, чтобы убедиться, что значение равно либо 0, либо 1.Если вас просто интересует 0 / не 0, вы можете обойтись без сдвига.
Взгляните на ответы на этот вопрос.
Теория
Не существует синтаксиса C для доступа или установки n-го бита встроенного типа данных (напримерa "символ").Однако вы можете получить доступ к битам с помощью логической операции "И" и установить биты с помощью логической операции "ИЛИ".
В качестве примера предположим, что у вас есть переменная, которая содержит 1101, и вы хотите проверить 2-й бит слева.Просто выполните логическое И с 0100:
1101
0100
---- AND
0100
Если результат отличен от нуля, то, должно быть, был установлен 2-й бит;в противном случае это не было задано.
Если вы хотите установить 3-й бит слева, то выполните логическое ИЛИ с 0010:
1101
0010
---- OR
1111
Вы можете использовать операторы C && (для AND) и || (для OR) для выполнения этих задач.Вам нужно будет самостоятельно создать шаблоны доступа к битам (0100 и 0010 в приведенных выше примерах).Хитрость заключается в том, чтобы помнить, что младший значащий бит (LSB) отсчитывает 1 секунду, следующий LSB отсчитывает 2 секунды, затем 4 секунды и т.д.Итак, шаблон доступа к битам для n-го LSB (начинающийся с 0) - это просто значение 2 ^ n.Самый простой способ вычислить это на C - сдвинуть двоичное значение 0001 (в этом четырехразрядном примере) влево на требуемое количество мест.Поскольку это значение всегда равно 1 в целочисленных величинах без знака, это просто '1 << н'
Пример
unsigned char myVal = 0x65; /* in hex; this is 01100101 in binary. */
/* Q: is the 3-rd least significant bit set (again, the LSB is the 0th bit)? */
unsigned char pattern = 1;
pattern <<= 3; /* Shift pattern left by three places.*/
if(myVal && (char)(1<<3)) {printf("Yes!\n");} /* Perform the test. */
/* Set the most significant bit. */
myVal |= (char)(1<<7);
Этот пример не был протестирован, но должен послужить иллюстрацией общей идеи.
Для запроса состояния бита с определенным индексом:
int index_state = variable & ( 1 << bit_index );
Для установки бита:
varabile |= 1 << bit_index;
Для перезапуска бита:
variable &= ~( 1 << bit_index );
Отдельные биты могут быть проиндексированы следующим образом.
Определите структуру, подобную этой:
struct
{
unsigned bit0 : 1;
unsigned bit1 : 1;
unsigned bit2 : 1;
unsigned bit3 : 1;
unsigned reserved : 28;
} bitPattern;
Теперь, если я хочу узнать отдельные битовые значения переменной с именем "value", выполните следующие действия:
CopyMemory( &input, &value, sizeof(value) );
Чтобы узнать, является ли бит 2 высоким или низким:
int state = bitPattern.bit2;
Надеюсь, это поможет.
Попробуйте использовать битовые поля.Будьте осторожны, реализация может варьироваться в зависимости от компилятора.
http://publications.gbdirect.co.uk/c_book/chapter6/bitfields.html
ЕСЛИ вы хотите немного проиндексировать, вы могли бы:
bit = (char & 0xF0) >> 7;
возвращает msb символа.Вы могли бы даже опустить правую смену и выполнить тест на 0.
bit = char & 0xF0;
если бит установлен, то результат будет > 0;
очевидно, вам нужно изменить маску, чтобы получить другие биты (NB:0xF - это битовая маска, если она неясна).Можно определить множество масок, например
#define BIT_0 0x1 // or 1 << 0
#define BIT_1 0x2 // or 1 << 1
#define BIT_2 0x4 // or 1 << 2
#define BIT_3 0x8 // or 1 << 3
и т.д...
Это дает вам:
bit = char & BIT_1;
Вы можете использовать эти определения в приведенном выше коде для успешной индексации бита либо в макросе, либо в функции.
Чтобы установить бит:
char |= BIT_2;
Чтобы немного очистить:
char &= ~BIT_3
Чтобы немного переключиться
char ^= BIT_4
Эта помощь?
Существует стандартный библиотечный контейнер для битов:std:: вектор.Он специализируется на том, чтобы библиотека была компактной.Существует также класс boost dynamic_bitset.
Это позволит вам выполнять операции с набором логических значений, используя один бит на значение базового хранилища.
Документация по динамическому набору битов Boost
Документацию по STL смотрите в документации вашего компилятора.
Конечно, вы также можете обращаться к отдельным битам в других целых типах вручную.Если вы сделаете это, вам следует использовать неподписанные типы, чтобы не получить неопределенное поведение, если вы решите выполнить сдвиг вправо для значения с установленным старшим битом.Однако, похоже, вам нужны контейнеры.
Комментатору, который утверждал, что это занимает в 32 раза больше места, чем необходимо:boost::dynamic_bitset и vector специализированы на использовании одного бита для каждой записи, и поэтому нет штрафа за пробел, предполагая, что вы на самом деле хотите больше, чем количество битов в примитивном типе.Эти классы позволяют вам адресовать отдельные биты в большом контейнере с эффективным базовым хранилищем.Если вам просто нужны (скажем) 32 бита, во что бы то ни стало используйте int .Если вам нужно какое-то большое количество битов, вы можете использовать библиотечный контейнер.