Побитовое ИЛИ (|) применяет операцию логического сложения к каждому биту операндов. Бит в позиции результата получает значение 1, если хотя бы один из соответствующих битов операндов равен 1, и 0, если биты обоих операндов равны 0. (Побитовое ИЛИ не нужно смешивать с логическим ИЛИ.)
Рассмотрим простой пример. Пусть у нас есть класс из 30 студентов. Каждую неделю преподаватель проводит зачет, результат которого – сдал/не сдал. Итоги можно представить в виде битового вектора. (Заметим, что нумерация битов начинается с нуля, первый бит на самом деле является вторым по счету. Однако для удобства мы не будем использовать нулевой бит; таким образом, студенту номер 1 соответствует бит номер 1. В конце концов, наш преподаватель – не специалист в области программирования.)
unsigned int quiz1 = 0;
Нам нужно иметь возможность менять значение каждого бита и проверять это значение. Предположим, студент 27 сдал зачет. Бит 27 необходимо выставить в 1, не меняя значения других битов. Это можно сделать за два шага. Сначала нужно начать с числа, содержащего 1 в 27-м бите и 0 в остальных. Для этого используем операцию сдвига:
1 27;
Применив побитовую операцию ИЛИ к переменной quiz1 и нашей константе, получим нужный результат: значение 27-й бита станет равным значение 1, а другие биты останутся неизменными.
quiz1 |= 127;
Теперь представим себе, что преподаватель перепроверил результаты теста и выяснил, что студент 27 зачет не сдал. Теперь нужно присвоить нуль 27-му биту, не трогая остальных. Сначала применим побитовое НЕ к предыдущей константе и получим число, в котором все биты, кроме 27-го, равны 1:
~(127 );
Теперь побитово умножим (И) эту константу на quiz1 и получим нужный результат: 0 в 27-м бите и неизменные значения остальных.
quiz1 = ~(127);
Как проверить значение того же 27-го бита? Побитовое И дает true, если 27-й бит равен 1, и false, если 0:
bool hasPassed = quiz1 (127);
При использовании побитовых операций подобным образом очень легко допустить ошибку. Поэтому чаще всего такие операции инкапсулируются в макросы препроцессора или встроенные функции:
inline boo1 bit_on (unsigned int ui, int pos)
{
return u1 ( 1 pos );
}
Вот пример использования:
enum students { Danny = 1, Jeffrey, Ethan, Zev, Ebie, // ...
AnnaP = 26, AnnaL = 27 };
const int student_size = 27;
// наш битовый вектор начинается с 1
bool has_passed_quiz[ student_size+l ];
for ( int index = 1; index = student_size; ++-index )
has_passed_quiz[ index ] = bit_on( quiz1, index );
Раз уж мы начали инкапсулировать действия с битовым вектором в функции, следующим шагом нужно создать класс. Стандартная библиотека С++ включает такой класс bitset, его использование описано ниже.
Даны два целых числа:
unsigned int ui1 = 3, ui2 = 7;
Каков результат следующих выражений?
(a) ui1 ui2 (c) uil | ui2
(b) ui1 ui2 (d) uil || ui2
Используя пример функции bit_on(), создайте функции bit_turn_on() (выставляет бит в 1), bit_turn_off() (сбрасывает бит в 0), flip_bit() (меняет значение на противоположное) и bit_off() (возвращает true, если бит равен 0). Напишите программу, использующую ваши функции.
В чем недостаток функций из предыдущего упражнения, использующих тип unsigned int? Их реализацию можно улучшить, используя определение типа с помощью typedef или механизм функций-шаблонов. Перепишите функцию bit_on(),применив сначала typedef, а затем механизм шаблонов.
4.12. Класс bitset
|
Операция |
Значение |
Использование |
|
test(pos) |
Бит pos равен 1? |
a.test(4) |
|
any() |
Хотя бы один бит равен 1? |
a.any() |
|
none() |
Ни один бит не равен 1? |
a.none() |
|
count() |
Количество битов, равных 1 |
a.count() |
|
size() |
Общее количество битов |
a.size() |
|
[pos] |
Доступ к биту pos |
a[4] |
|
flip() |
Изменить значения всех |
a.flip() |
|
flip(pos) |
Изменить значение бита pos a.fli |
p(4) |
|
set() |
Выставить все биты в 1 |
a.set() |
|
set(pos) |
Выставить бит pos в 1 a.se |
t(4) |
|
reset() |