Аналогичные действия выполняются и в случае применения оператора |, только результат вычисляется несколько иначе (см. табл. 2.5).
В качестве примера рассмотрим операцию, выполняемую
следующим кодом:
int result, op1, ор2; op1 = 4; ор2 = 5; result = opl & op2;
Здесь необходимо принять во внимание двоичное представление операндов op1 и ор2, соответственно 100 и 101.
Результат этой операции получается путем сравнения двоичных
Бит операнда 1 | Бит операнда 2 | Бит результата выполнения операции |
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
Таблица 2.6-Битовая операция & битов, находящихся в одинаковых позициях в каждом из этих логических представлений:
- первому слева биту результата присваивается значение 1, если оба крайних левых бита op1 и ор2 равны 1, и значение 0 в противном случае;
- следующему биту результата присваивается значение 1, если оба следующих бита op1 и ор2 равны 1, и значение
0 в противном случае;
Таблица 2.7–Логический оператор ~
Бит операнда | Бит результата выполнения операции |
1 | 0 |
0 | 1 |
- так продолжается для всех остальных битов.
В данном примере крайние биты слева у обоих операндов op1 и ор2 равны 1, поэтому крайний левый бит результата также будет равен 1. В следующей позиции оба бита равны 0, а в третьей позиции биты равны
Биты | Десятичное представление | Значение |
000 | 0 | Черный |
100 | 4 | Красный |
010 | 2 | Зеленый |
001 | 1 | Синий |
101 | 5 | Пурпурный |
по | 6 | Желтый |
011 | 3 | Голубой |
111 | 7 | Белый |
Таблица 2.8–Трехбитовое представление цвета соответственно 1 и 0; таким образом, второй и третий биты результата будут равны 0. В качестве окончательного результата в двоичном представлении будет получено число 100, т. е. результат выполнения этой операции будет равен 4.
Аналогичный процесс происходит при использовании оператора |, с тем лишь отличием, что получающийся в результате бит равняется 1 в том случае, если хотя бы один из соответствующих битов операндов равен 1.
Точно таким же способом можно использовать и оператор ^. В этом случае в результате появляется 1, если только у одного из операндов, но не у обоих одновременно,
в соответствующей позиции находится единица.
В языке С# допускается использование унарного оператора работы с битами ~, воздействие которого на операнд состоит в инвертировании (изменении) всех его битов таким образом, что все биты, которые были равны 0, получают значение 1, и наоборот (см. табл. 2.7).
Такие побитовые операции оказываются весьма полезными в самых разных ситуациях, поскольку они предлагают простой метод использования отдельных битов переменной для хранения информации. Рассмотрим простое представление цвета, в котором используется три бита для указания наличия красной, зеленой и синей составляющих. У нас появляется возможность задавать каждый из этих битов независимо и изменить цвет, определяемый всеми тремя битами, на один из следующих вариантов (см. табл. 2.8).
Предположим, что мы храним эти значения в переменной типа int. Начиная с черного цвета или, что то же самое, со значения переменной типа int, равного 0, можно выполнить различные операции. Например:
int myColor = 0; bool containsRed; myColor = myColor | 2;
// Добавлен бит зеленого цвета, значение переменной myColor стало 010 myColor = myColor | 4;
// Добавлен бит красного цвета, значение переменной myColor стало 110 containsRed = (myColor & 4) = = 4;// Выполняется проверка на наличие бита, отвечающего за красный цвет
В результате выполнения последней строки кода значение переменной containsRed будет true, поскольку "красный" бит в переменной myColor равен 1.
Этот способ может оказаться весьма полезным в том случае, если выполняемые операции используются для одновременной проверки нескольких битов (например, для 32 в случае значений типа int). Однако существуют более эффективные способы хранения дополнительной информации в отдельных переменных, для чего используются переменные сложных типов.
Помимо описанных выше четырех операторов для работы с битами следует рассмотреть еще два оператора (табл.
2.9).
Таблица 2.9 – Операторы побитового сдвига
Оператор | Тип | Пример выражения | Результат |
>> | Бинарный | varl = var2 >> var3; | Переменной varl присваивается значение, которое получается при сдвиге двоичного содержимого переменной var2 вправо на число битов, равное значению переменной var3. |
<< | Бинарный | varl = var2 << var3; | Переменной varl присваивается значение, которое получается при сдвиге двоичного содержимого переменной var2 влево на число битов, равное значению переменной var3. |
Работу этих операторов, обычно называемых операторами побитового сдвига, лучше всего проиллюстрировать с помощью небольшого примера:
int varl, var2 =10, var3 = 2; | |
var1 = var2 << var3; |
В этом примере переменной varl будет присвоено значение 40. Действительно, двоичное представление числа 10 – "1010"; после сдвига на две позиции влево оно превратится в "101000" – двоичное представление числа 40. То, что мы сделали, можно изобразить как операцию умножения. Каждый бит при сдвиге на одну позицию влево увеличивает свое значение в два раза, соответственно сдвиг на две позиции эквивалентен умножению на 4. И наоборот, при сдвиге всех битов вправо возникает эффект деления операнда на два, при этом любой целый остаток теряется:
int varl, var2 =10; varl = var2 >> 1;
В данном случае varl будет содержать значение 5, а в результате выполнения следующего примера ее значение станет равным 2:int varl, var2 = 10; varl = var2 >>2;
Маловероятно, что в настоящее время активно пользуются этими операторами, однако знать об их существовании полезно. Их применяют главным образом для получения предельно оптимизированного кода, когда использование других математических операций из-за их большей затратности оказывается неприемлемым. По этой причине рассмотренные здесь операции часто задействуют, например, в драйверах устройств или системных кодах.
2.2.1.Логические операторы присваивания
Последние операторы - это комбинация рассмотренных выше операторов с оператором присваивания. Они во многом сходны с математическими операторами присваивания, рассматривавшимися в предыдущей лекции ( + = , *= и т.д.). Логические операторы приведены в таблице 2.10.
Таблица 2.10 – Логические операторы присваивания
Оператор | Тип | Пример выражения | Результат |
&= | Бинарный | varl &= var2; | Переменной varl присваивается значение, являющееся результатом выполнения операции varl & var2. |
|= | Бинарный | var1 |= var2; | Переменной varl присваивается значение, являющееся результатом выполнения операции varl I var2. |
^= | Бинарный | varl ^= var2; | Переменной varl присваивается значение, являющееся результатом выполнения операции varl * var2. |
Эти операторы используются как с логическими, так и с численными значениями, точно так же, как и операторы &, | и ^.
При выполнении операторов &= и |= применяются операторы & и |, а не && и ||, что приводит к издержкам, характерным для этих более простых операторов.
Для операторов побитового сдвига также существуют соответствующие операторы присваивания (табл. 2.11).
Таблица 2.11 – Операторы побитового присваивания
Оператор | Тип | Пример выражения | Результат |
>>= | Унарный | varl >> var2; | Переменной varl присваивается значение, которое получается в результате сдвига двоичного содержимого переменной varl вправо на число битов, равное значению переменной var2. |
<<= | Унарный | varl << var2; | Переменной varl присваивается значение, которое получается в результате сдвига двоичного содержимого переменной varl влево на число битов, равное значению переменной var2. |
2.2.2.Старшинство операторов с дополнениями Старшинство операторов приведено в таблице 2.12.