Шаг 26.
Язык программирования C#. Начала
Базовые типы и операторы. Побитовые операторы и двоичные коды (окончание)

    На этом шаге мы рассмотрим особенности использования побитовых операторов.

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

Таблица 1. Побитовые операторы
Оператор Описание
& Оператор "побитовое и". Результатом выражения А&В является число, которое получается сравнением соответствующих битов в представлении чисел А и В. На выходе получается единичный бит, если оба сравниваемых бита единичные. Если хотя бы один из сравниваемых битов нулевой, в результате получаем нулевой бит
| Оператор "побитовое или". Значение выражения вида А | В есть число, получаемое сравнением соответствующих битов в представлении чисел А и В. На выходе получается единичный бит, если хотя бы один из сравниваемых битов единичный. Если оба сравниваемых бита нулевые, в результате получаем нулевой бит
^ Оператор "побитовое исключающее или". Значением выражения А^В является число результат сравнения соответствующих битов в представлении чисел А и В. На выходе получается единичный бит, если сравниваемые биты различны (один единичный, а другой нулевой). Если биты одинаковые (оба единичные или оба нулевые), то в результате получаем нулевой бит
~ Оператор "побитовая инверсия". Значение выражения вида есть число, битовое представление которого получается заменой в битовом представлении числа А нулей на единицы и единиц на нули. Значение операнда А при этом не меняется
<< Оператор "сдвиг влево". Результатом выражения А<<B является число, которое получается путем сдвига бинарного представления числа А на B позиций влево. Младшие биты при этом заполняются нулями, а старшие теряются. Значение операнда А остается неизменным
>> Оператор "сдвиг вправо". Значение выражения А>>B равно числу, которое получается путем сдвига бинарного представления числа А на B позиций вправо. Младшие биты при этом теряются, а старшие заполняются значением старшего (знакового) бита. Значение операнда А остается неизменным

    Вообще побитовые операторы напоминают логические, с поправкой на то, что операции выполняются с битами, истинному значению соответствует 1, а ложному значению соответствует 0. Например, при вычислении выражения А&В на основе оператора "побитового и" в результате получается целое число. Его бинарный код вычисляется так: сравниваются биты, находящиеся на одинаковых позициях в представлении чисел А и В. Правила сравнения даются в таблице 2.

Таблица 2. Операция "побитовое и" (а&b) для битов а и b
a 1 0
b
1 1 0
0 0 0

    Если оба бита единичные, то в соответствующем месте в коде для числа-результата записывается единица. Если хотя бы один из двух сравниваемых битов нулевой, то в число-результат записывается нулевой бит.


Оператор "логические и" и "побитовое и" записываются одним и тем же символом &. О каком операторе идет речь, можно понять по операндам. Если операнды числовые, то это побитовый оператор. Если операнды логические, то это логический оператор. Это же замечание относится и к рассматриваемым далее операторам | и ^.

    Как пример простой операции с использованием оператора "побитового и" & рассмотрим следующий блок программного кода:

  byte А=13, В=25, С;
  С=(byte)(А&В);

    В данном случае имеем дело с тремя переменными А, В и С типа byte. Это значит, что значение каждой из переменных запоминается с помощью 8 битов.


Хотя переменные А и В объявлены как относящиеся к типу byte, при выполнении операции А&В из-за автоматического приведения типа результат вычисляется как int-значение. Это значение мы пытаемся записать в переменную С типа byte. Поэтому здесь необходима инструкция (byte) явного приведения типа. Это замечание относится и к нескольким следующим мини-примерам.

    Десятичное число 13 в двоичном 8-битовом коде записывается как 00001101, а числу 25 соответствует бинарный код 00011001.


Определить бинарные коды для чисел 13 и 25 можно, если учесть что 13 = 8 + 4 + 1= 23 + 22 + 20 = 0*27 + 0*26 + 0*25 + 0*24+1*23+1*22 + 0*21 + 1*20, а также 25 = 16 + 8 + 1 = 24 + 2*3 + 20 = 0*27 + 0*26 + 0*25 + 1*24 + 1*23 + 0*22 + 0*21 + 1*20.

    Операция "побитовое и" выполняется так:

    00001101
&   00011001
   ----------
    00001001

    Следовательно, в результате получаем бинарный код 00001001. Несложно проверить, что это код числа 23 + 20 = 9. Таким образом, при выполнении указанного выше программного кода значения переменных будут следующими: значение переменной А равно 13, значение переменной В равно 25, а значение переменной С равно 9.

    Если используется оператор "побитовое или" |, то при сравнении двух битов в результате получаем единичный бит, если хотя бы один из сравниваемых битов единичный. Правила выполнения этой операции проиллюстрированы в таблице 3.

Таблица 3. Операция "побитовое или" (а|b) для битов а и b
a 1 0
b
1 1 1
0 1 0

    Рассмотрим, как операция "побитовое или" выполняется с уже знакомыми для нас переменными:

  byte А=13, В=25, С;
  С=(byte)(А|В);

    В соответствии с правилами выполнения операции получаем следующий результат:

     00001101
 |   00011001
    ----------
     00011101

    Бинарный код 00011101 соответствует десятичному числу 29, поскольку имеет место соотношение 24 + 23 + 22 + 20 = 29. Таким образом, переменная С в данном случае будет иметь значение 29.

    Если выполняется операция "побитовое исключающее или", то двоичные коды для двух исходных чисел сравниваются с использованием такой схемы: если на одинаковых позициях в битовых представлениях чисел значения разные (то есть один бит единичный, а другой нулевой), то на "выходе" получаем единицу. Если у сравниваемых чисел на одинаковых позициях находятся одинаковые биты (то есть биты с одинаковыми значениями), в числе-результате на соответствующем месте будет нулевой бит (бит с нулевым значением). Правила выполнения операции "побитовое исключающее или" проиллюстрированы в таблице 4 для двух отдельных битов.

Таблица 4. Операция "побитовое исключающее или" (а^b) для битов а и b
a 1 0
b
1 0 1
0 1 0

    Результат применения операции "побитовое исключающее или" к числовым значениям 13 и 25 приводит к следующему результату:

   00001101 
 ^ 00011001
  ----------
   00010100

    Мы получаем бинарный код 00010100, который соответствует десятичному числу 20 (поскольку 24 + 22 = 20). Допустим, мы имеем дело, например. с таким блоком программного кода:

  byte А=13, В=25, С;
  С=(byte)(А^В);

    В результате его выполнения переменная С получит значение 20.

    Оператор "побитовая инверсия" является унарным. Побитовая инверсия выполняется просто: в битовом представлении числа нули меняются на единицы, а единицы меняются на нули. Это правило, по которому вычисляется результат. Операнд при этом не меняется. В таблице 5 показано, как операция побитовой инверсии выполняется для одного отдельного бита.

Таблица 5. Операция "побитовая инверсия" (~а) для бита а
a 1 0
~a 0 1

    Например, имеется такой блок программного кода:

  byte А, В;
  A=13;
  B=(byte)~А;

    Вопрос состоит в том, какие значения будут у целочисленных переменных А и В после выполнения указанных команд? Ответ, в принципе, простой: значение переменной А равно 13, а значение переменной В равно 242. Самая простая ситуация с переменной А. Ей присваивается значение 13. Это число в десятичной системе. В двоичной системе код значения переменной А имеет вид 00001101. Но в данном случае это не столь важно, поскольку при выполнении команды В=(byte)~А значение переменной А не меняется: побитовая инверсия применяется для вычисления результата выражения , но сам операнд А не меняется. Поэтому переменная А остается со своим, ранее присвоенным значением (число 13).


Напомним, что значение типа byte записывается с использованием 8 битов и записанное таким образом число интерпретируется как неотрицательное число. Поэтому старший бит в представлении byte-числа знаковым не является.

    Если применить операцию побитового инвертирования к коду 00001101 числа 13, то в бинарном 8-битовом представлении иолучим код 11110010. Этот код соответствует числу 27 + 26 + 25 + 24 + 21 = 128 + 64 + 32 + 16 + 2 = 242. Таким образом, переменная В получает значение 242. Но еще раз подчеркнем, что в данном случае важно, что тип byte предназначен для реализации неотрицательных чисел. Если мы вместо типа byte воспользуемся типом sbyte, результат побитовой инверсии будет иным. Точнее, он будет по-другому интерпретироваться. Допустим, имеется такой код:

  sbyte А, В;
  A=13;
  B=(sbyte)~А;

    Вопрос состоит в том, каково значение переменной B? Строго говоря, как и в предыдущем случае, значение переменной В будет представлено кодом 11110010. Но теперь, поскольку это sbyte-значение, код интерпретируется как число со знаком. Так как старший бит единичный, то число отрицательное. Чтобы понять, что это за число, мы должны:

    Поскольку нам предстоит инвертировать то, что было получено инвертированием, мы сначала получим бинарный код для числа 13, затем прибавим к этому числу 1 (получим 14) и, дописав знак "минус", получаем значение -14 для переменной В.

    Еще две побитовые операции, которые нам осталось обсудить - сдвиг влево и сдвиг вправо. Например, рассмотрим следующий программный код:

  byte А=13, В, С;
  B=(byte)(А<<2);
  C=(byte)(А>>2);

    Чтобы получить значение переменной B, мы должны взять код 00001101 для числа 13 (значение переменной А) и выполнить сдвиг этого кода влево на две позиции. В результате получаем код 00110100. Это код числа 52 - значение переменной В. Значение переменной A остается неизменным.


Сдвиг влево на одну позицию бинарного кода числа эквивалентен умножению числа на 2. Сдвиг бинарного кода положительного числа на одну позицию вправо эквивалентен целочисленному делению этого числа на 2.

    Для вычисления значения переменной С бинарный код значения переменной А сдвигается вправо на две позиции. В результате из кода 00001101 получаем код 00000011, что соответствует числу 3. Это и есть значение переменной С.

    На следующем шаге мы рассмотрим оператор присваивания.




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