Шаг 15.
Битовые операции

    Этот шаг посвящен использованию битовых операций.

    Использование битовых операций обычно свойственно программистам, работающим на языке ассемблера (например, если необходимо проверить разряды регистров состояний или маскировать некоторые из разрядов при приеме и передаче информации). Однако, для некоторых программ необходима (или, по крайней мере, полезна) возможность манипулировать отдельными разрядами в байте или слове. Например, часто варианты режимов устройства ввода-вывода устанавливаются байтом, в котором каждый разряд действует как признак "включено-выключено".

    В языке C++ существуют следующие операции, выполняющиеся над разрядами:

Остановимся на данных операциях более подробно.

    1. Инверсия битов  (поразрядное отрицание, дополнение до единицы) инвертирует биты, т.е. каждый бит со значением 1 получает значение 0 и наоборот.

    2. Битовое "И" сравнивает последовательно разряд за разрядом два операнда. Для каждого разряда результат равен 1, тогда и только тогда, когда оба соответствующих разряда операндов равны 1. Так, например,

   10010011 & 00111101 = 00010001,
потому что только нулевой и четвертый разряды обоих операндов содержат 1.

    3. Битовое "ИЛИ" сравнивает последовательно разряд за разрядом два своих операндов. Для каждого разряда результат равен 1 тогда и только тогда, когда любой из соответствующих разрядов операндов равны 1. Так, например,

   10010011 | 00111101 = 10111111,
потому что все разряды (кроме шестого) в одном из двух операндов имеют значение 1.

    4. Битовое исключающее "ИЛИ" сравнивает последовательно разряд за разрядом два своих операндов. Для каждого разряда результат равен 1, если один из двух (но не оба) соответствующих разрядов операндов равен 1. Так, например,

   10010011 ^ 00111101 = 10101110 .
Заметим, что, поскольку нулевой разряд в обоих операндах имеет значение 1, нулевой разряд результата имеет значение 0.

Описанные выше операции часто используются для установки некоторых битов, причем другие биты остаются неизменными. Они удобны для фильтрации или маскирования битов.

    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 как последовательности двоичных цифр.


    Операции 1.
   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


    Операции 2.
   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


    Операции 3.
   x = 03; y = 02; z = 01;
   k = x^y&~z;
 
   k = (03^02);
   1


    Операции 4.
   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


    Операции 5.
   x = 01;
   k = !x|x;
 
   k = ((!x)|x);
   k = ((!TRUE)|x);
   k = (FALSE|01);
   k = (0|01);
   1


    Операции 6.
   x = 01;
   k = -x|x;

   k = ((-x)|x);
   k = (-01|01);
   -01 | 01  = 111111112 | 000000012 =  111111112 = -1
   -1


    Операции 7.
   x = 01;
   k = x^x;
 
   k = (01^01);
   0


    Операции 8.
   x = 01;
   x <<= 3;
 
   x = 01<<3;
   01 << 3 = 0000000012 << 3 =  000010002 = 8
   x = 8;
   8


    Операции 9.
   y = -01;
   y <<= 3;
 
   y = -01<<3
   -01 << 3 = 111111112 << 3 =  111110002 =  -8
   y = -8;
   -8


    Операции 10.
   y = -08;
   y >>= 3;
 
   y = -08>>3;
   -1

    В некоторых случаях вместо -1 может получиться другой результат (8191). Появление этого значения объясняется тем, что на некоторых компьютерах при операции сдвига знак числа может не сохраниться. Не все трансляторы языка C++ гарантируют, что операция сдвига арифметически корректна, поэтому в любом случае более ясным способом деления на 8 было бы явное деление y=y/8.

    На следующем шаге мы разберем использование условной операции.


Предыдущий шаг Содержание Следующий шаг