Этот шаг посвящен использованию битовых операций.
Использование битовых операций обычно свойственно программистам, работающим на языке ассемблера (например, если необходимо проверить разряды регистров состояний или маскировать некоторые из разрядов при приеме и передаче информации). Однако, для некоторых программ необходима (или, по крайней мере, полезна) возможность манипулировать отдельными разрядами в байте или слове. Например, часто варианты режимов устройства ввода-вывода устанавливаются байтом, в котором каждый разряд действует как признак "включено-выключено".
В языке C++ существуют следующие операции, выполняющиеся над разрядами:
1. Инверсия битов (поразрядное отрицание, дополнение до единицы) инвертирует биты, т.е. каждый бит со значением 1 получает значение 0 и наоборот.
2. Битовое "И" сравнивает последовательно разряд за разрядом два операнда. Для каждого разряда результат равен 1, тогда и только тогда, когда оба соответствующих разряда операндов равны 1. Так, например,
10010011 & 00111101 = 00010001,
3. Битовое "ИЛИ" сравнивает последовательно разряд за разрядом два своих операндов. Для каждого разряда результат равен 1 тогда и только тогда, когда любой из соответствующих разрядов операндов равны 1. Так, например,
10010011 | 00111101 = 10111111,
4. Битовое исключающее "ИЛИ" сравнивает последовательно разряд за разрядом два своих операндов. Для каждого разряда результат равен 1, если один из двух (но не оба) соответствующих разрядов операндов равен 1. Так, например,
10010011 ^ 00111101 = 10101110 .
Описанные выше операции часто используются для установки некоторых битов, причем другие биты остаются неизменными. Они удобны для фильтрации или маскирования битов.
5. Сдвиг влево сдвигает разряды левого операнда влево на число позиций, указанное правым операндом. Освобождающиеся позиции заполняются нулями, а разряды, сдвигаемые за левый предел левого операнда, теряются. Поэтому, например,
10001010 << 2 = 00101000 ,
Таким образом, х<<2 сдвигает х влево на 2 позиции, заполняя освобождающиеся позиции нулями (эквивалентно умножению на 4).
6. Сдвиг вправо сдвигает разряды левого операнда вправо на число позиций, указанное правым операндом. Разряды, сдвигаемые за правый предел левого операнда, теряются. Для чисел со знаком результат зависит от компилятора. Освобождающиеся позиции могут заполнятся нулями или значением знакового разряда (самого левого).
Для значений без знака имеем
10001010 >> 2 = 00100010 ,
Эти две операции выполняют сдвиг, а также эффективное умножение и деление на степени числа 2.
Пример 1.
#include<iostream.h> main () { int y=02,x=03,z=01,k; k = x|y&z; cout<<k<<" "; /* Операции 1 */ k = x|y&~z; cout<<k<<" "; /* Операции 2 */ k = x^y&~z; cout<<k<<" "; /* Операции 3 */ k = x&y&&z; cout<<k<<" "; /* Операции 4 */ x = 1; y = -1; k = !x|x; cout<<k<<" "; /* Операции 5 */ k = -x|x; cout<<k<<" "; /* Операции 6 */ k = x^x; cout<<k<<" "; /* Операции 7 */ x <<= 3; cout<<x<<" "; /* Операции 8 */ y <<= 3; cout<<y<<" "; /* Операции 9 */ y >>= 3; cout<<y<<" "; /* Операции 10 */ }
Результат работы программы:
3 3 1 1 1 -1 0 8 -8 -1
Комментарии. Целые константы, начинающиеся с цифры 0, являются восьмеричными числами. Восьмеричное представление целых чисел особенно удобно, когда приходится работать с поразрядными операциями, так как восьмеричные числа легко переводятся в двоичные. В этой задаче числа 01,02,03 соответствуют числам 1, 2 и 3, так что появление восьмеричных чисел служит намеком, что программа рассматривает значения x, y и z как последовательности двоичных цифр.
x = 03; y = 02; z = 01; k = x|y&z;
Вследствие приоритетов операций: k = (x|(y&z));. Самое внутреннее выражение вычисляется первым.
k = (x|(02&01)); 02 & 01 = 000000102 & 000000012 = 000000002 = 0 k = (x|00); 03 | 00 = 000000112 | 000000002 = 000000112 = 03 03
x = 03; y = 02; z = 01; k = x|y&~z; k = (x|(y&(~z))); ~000000012 = 111111102 02 & 111111102 = 0000000102 & 111111102 = 0000000102 = 02 03 | 02 = 000000112 | 0000000102 = 000000112 = 03 3
x = 03; y = 02; z = 01; k = x^y&~z; k = (03^02); 1
x = 03; y = 02; z = 01; k = x&y&&z; k = ((x&y)&&z); k = ((03&02)&&z); k = (02&&z); k = (TRUE&&z); k = (TRUE&&01); k = (TRUE&&TRUE) TRUE или 1
x = 01; k = !x|x; k = ((!x)|x); k = ((!TRUE)|x); k = (FALSE|01); k = (0|01); 1
x = 01; k = -x|x; k = ((-x)|x); k = (-01|01); -01 | 01 = 111111112 | 000000012 = 111111112 = -1 -1
x = 01; k = x^x; k = (01^01); 0
x = 01; x <<= 3; x = 01<<3; 01 << 3 = 0000000012 << 3 = 000010002 = 8 x = 8; 8
y = -01; y <<= 3; y = -01<<3 -01 << 3 = 111111112 << 3 = 111110002 = -8 y = -8; -8
y = -08; y >>= 3; y = -08>>3; -1
В некоторых случаях вместо -1 может получиться другой результат (8191).
Появление
этого значения объясняется тем, что на некоторых компьютерах при операции
сдвига
знак числа может не сохраниться. Не все трансляторы языка C++ гарантируют,
что операция сдвига арифметически корректна, поэтому в любом случае более
ясным способом
деления на 8 было бы явное деление y=y/8.
На следующем шаге мы разберем использование условной операции.