Я пытаюсь реализовать идею сжатия данных, которая у меня была, и, поскольку я представляю, как запустить ее с большим корпусом тестовых данных, я подумал написать ее на C (в основном у меня есть опыт работы с такими языками сценариев, как Ruby и Tcl.)
Просматривая «коровьи» книги О'Рейли по C, я понимаю, что я не могу просто проиндексировать биты простой переменной типа char или int, как я хотел бы делать побитовые сравнения и операторы.
Прав ли я в этом восприятии? Разумно ли для меня использовать перечислимый тип для представления бита (и создать из них массив, а также написать функции для преобразования в и из char)? Если да, то есть ли где-то такой тип и функции, определенные в стандартной библиотеке? Есть ли другие (лучшие?) Подходы? Есть ли где-нибудь пример кода, на который мне могли бы указать?
Спасибо -





Это возможно.
Чтобы установить 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 и AND:
(x & (1 << 5)) >> 5 // gets the value (0 or 1) of the 5th-from-right
примечание: сдвиг вправо 5 должен гарантировать, что значение равно 0 или 1. Если вас интересует просто 0 / не 0, вы можете обойтись без сдвига.
ЕСЛИ вы хотите немного проиндексировать, вы можете:
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
Это поможет?
Посмотрите ответы на этот вопрос.
Чтобы запросить состояние бита с определенным индексом:
int index_state = variable & ( 1 << bit_index );
Чтобы установить бит:
varabile |= 1 << bit_index;
Чтобы перезапустить бит:
variable &= ~( 1 << bit_index );
Есть стандартный библиотечный контейнер для битов: std :: vector. Он специализируется на библиотеке, чтобы экономить пространство. Также есть класс boost dynamic_bitset.
Это позволит вам выполнять операции с набором логических значений, используя один бит на значение базового хранилища.
Документация по ускорению динамического набора битов
Документацию по STL см. В документации к вашему компилятору.
Конечно, вы также можете вручную адресовать отдельные биты в других целочисленных типах. Если вы это сделаете, вам следует использовать беззнаковые типы, чтобы не получить неопределенное поведение, если вы решите выполнить сдвиг вправо для значения с установленным старшим битом. Однако похоже, что вам нужны контейнеры.
Комментатору, заявившему, что это занимает в 32 раза больше места, чем необходимо: boost :: dynamic_bitset и vector специализируются на использовании одного бита на запись, и поэтому нет штрафа за место, если предположить, что вы действительно хотите больше, чем количество бит в примитивный тип. Эти классы позволяют адресовать отдельные биты в большом контейнере с эффективным базовым хранилищем. Если вы просто хотите (скажем) 32 бита, во что бы то ни стало, используйте int. Если вам нужно большое количество битов, вы можете использовать контейнер библиотеки.
Нет, дело в vector <bool> и boost :: dynamic_bitset в том, что они используют один бит на bool. Для хранения 1024 bool они будут использовать 128 байтов плюс служебные данные класса. Как вы рассчитали в 32 раза больше места для хранения, чем необходимо?
Попробуйте использовать битовые поля. Будьте осторожны, реализация может варьироваться в зависимости от компилятора.
http://publications.gbdirect.co.uk/c_book/chapter6/bitfields.html
Теория
Не существует синтаксиса C для доступа или установки n-го бита встроенного типа данных (например, 'char'). Однако вы можете получить доступ к битам с помощью логической операции И и установить биты с помощью операции логического ИЛИ.
В качестве примера предположим, что у вас есть переменная, содержащая 1101, и вы хотите проверить второй бит слева. Просто выполните логическое И с 0100:
1101
0100
---- AND
0100
Если результат не равен нулю, значит, должен быть установлен 2-й бит; в противном случае не было установлено.
Если вы хотите установить третий бит слева, выполните логическое ИЛИ с 0010:
1101
0010
---- OR
1111
Вы можете использовать операторы C && (для AND) и || (для OR) для выполнения этих задач. Вам нужно будет создать шаблоны битового доступа (0100 и 0010 в приведенных выше примерах) самостоятельно. Хитрость заключается в том, чтобы помнить, что младший значащий бит (LSB) считается 1 с, следующий LSB - 2, затем 4 и т. д. Таким образом, шаблон доступа к битам для n-го LSB (начиная с 0) - это просто значение 2 ^ п. Самый простой способ вычислить это в C - сдвинуть двоичное значение 0001 (в этом четырехбитном примере) влево на необходимое количество разрядов. Поскольку это значение всегда равно 1 в беззнаковых целочисленных величинах, это просто '1 << n'
Пример
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);
Этот пример не тестировался, но должен служить для иллюстрации общей идеи.
Следуя тому, что сказал Кайл, вы можете использовать макрос, который сделает за вас тяжелую работу.
It is possible.
To set the nth bit, use OR:
x |= (1 << 5); // sets the 6th-from right
To clear a bit, use AND:
x &= ~(1 << 5); // clears 6th-from-right
To flip a bit, use XOR:
x ^= (1 << 5); // flips 6th-from-right
Или же...
#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
}
Отдельные биты можно индексировать следующим образом.
Определите такую структуру:
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;
Надеюсь это поможет.
При этом используется в 32 раза больше данных, чем необходимо. В оригинальном плакате говорилось, что его интересует сжатие данных. Это похоже на обратное!