Следует подчеркнуть, что оператор
&
отличается от оператора
&&
, а оператор
|
отличается от оператора
||
тем, что они применяются к каждому биту своих операндов по отдельности (раздел A.5.5), а их результат состоит из такого же количества битов, что и операнды. В противоположность этому операторы
&&
и
||
просто возвращают значение
true
или
false
.
Рассмотрим несколько примеров. Обычно битовые комбинации выражаются в шестнадцатеричном виде. Для полубайта (четыре бита) используются следующие коды.
Для представления чисел, не превышающих девяти, можно было бы просто использовать десятичные цифры, но шестнадцатеричное представление позволяет не забывать, что мы работаем с битовыми комбинациями. Для байтов и слов шестнадцатеричное представление становится действительно полезным. Биты, входящие в состав байта, можно выразить с помощью двух шестнадцатеричных цифр.
Итак, используя для простоты тип
unsigned
(раздел 25.5.3), можем написать следующий фрагмент кода:
unsigned char a = 0xaa;
unsigned char x0 = ~a; // дополнение a
unsigned char b = 0x0f;
unsigned char x1 = a&b; // a и b
unsigned char x2 = a^b; // исключительное или: a xor b
unsigned char x3 = a<<1; // сдвиг влево на один разряд
Вместо бита, который был “вытолкнут” с самой старшей позиции, в самой младшей позиции появляется нуль, так что байт остается заполненным, а крайний левый бит (седьмой) просто исчезает.
unsigned char x4 == a>>2; // сдвиг вправо на два разряда
В двух позициях старших битов появились нули, которые обеспечивают заполнение байта, а крайние правые биты (первый и нулевой) просто исчезают.
Мы можем написать много битовых комбинаций и потренироваться в выполнении операций над ними, но это занятие скоро наскучит. Рассмотрим маленькую программу, переводящую целые числа в их битовое представление.
int main()
{
int i;
while (cin>>i)
cout << dec << i << "=="
<< hex << "0x" << i << "=="
<< bitset<8*sizeof(int)>(i) << '\n';
}
Для того чтобы вывести на печать отдельные биты целого числа, используется класс
bitset
из стандартной библиотеки.
bitset<8*sizeof(int)>(i)
Класс
bitset
хранит фиксированное количество битов. В данном случае мы использовали количество битов, равное размеру типа int —
8*sizeof(int)
, — и инициализировали объект класса
bitset
целым числом
i
.
ПОПРОБУЙТЕ
Скомпилируйте программу для работы с битовыми комбинациями и попробуйте создать двоичные и шестнадцатеричные представления нескольких чисел. Если вас затрудняет представление отрицательных чисел, перечитайте раздел 25.5.3 и попробуйте снова.
25.5.2. Класс bitset
Для представления наборов битов и работы с ними используется стандартный шаблонный класс
bitset
из заголовка
<bitset>
. Каждый объект класса
bitset
имеет фиксированный размер, указанный при его создании.
bitset<4> flags;
bitset<128> dword_bits;
bitset<12345> lots;
Объект класса
bitset
по умолчанию инициализируется одними нулями, но обычно у него есть инициализатор. Инициализаторами объектов класса
bitset
могут быть целые числа без знака или строки, состоящие из нулей и единиц:
bitset<4> flags = 0xb;
bitset<128> dword_bits(string("1010101010101010"));
bitset<12345> lots;
Здесь объект
lots
будет содержать одни нули, а
dword_bits
— 112 нулей, за которыми следуют 16 явно заданных битов. Если вы попытаетесь проинициализировать объект класса
bitset
строкой, состоящей из символов, отличающихся от
'0'
и
'1'
, то будет сгенерировано исключение
std::invalid_argument
.
string s;
cin>>s;
bitset<12345> my_bits(s); // может генерировать исключение
// std::invalid_argument
К объектам класса
bitset
можно применять обычные операции над битами. Предположим, что переменные
b1
,
b2
и
b3
являются объектами класса
bitset
.