1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 19
Текст из файла (страница 19)
Операторы91num после сброса младшего бита: 4num: 6num после сброса младшего бита: 6num: 7num после сброса младшего бита: 6num: 8num после сброса младшего бита: 8num: 9num после сброса младшего бита: 8num: 10num после сброса младшего бита: 10Значение 0xFFFE, используемое в этой программе, в двоичном коде представляетсячислом 1111 1111 1111 1110. Таким образом, операция num & 0xFFFE оставляет всебиты неизменными за исключением младшего, который устанавливается в нуль.
Поэтомулюбое четное число, пройдя через это “чистилище”, остается четным, а любое нечетное“выходит” из него уже четным (за счет уменьшения на единицу).Оператор И также используется для определения значения разряда. Например,следующая программа определяет, является ли заданное число нечетным.// Использование поразрядного оператора И для// определения, является ли число нечетным.using System;class IsOdd {public static void Main() {ushort num;num = 10;if((num & 1) == 1)Console.WriteLine("Этот текст не будет отображен.");num = 11;if((num & 1) == 1)Console.WriteLine(num + " -- нечетное число.");}}Результат выполнения этой программы выглядит так:11 -- нечетное число.В обеих инструкциях if выполняется операция И для значения переменной num ичисла 1. Если младший бит переменной num установлен (т.е.
равен единице), результатоперации num & 1 также будет равен единице. В противном случае результат будет равеннулю. Условие инструкции if выполнится только в случае, если анализируемое числоокажется нечетным.Возможности поразрядного тестирования, которые предоставляет поразрядныйоператор &, можно использовать для создания программы, которая отображает значениетипа byte в двоичном формате.
Рассмотрим один из возможных вариантов решения этойзадачи.92Часть I. Язык C#// Отображение значений битов, составляющих байт.using System;class ShowBits {public static void Main() {int t;byte val;val = 123;}}for(t = 128; t > 0; t = t/2) {if((val & t) != 0) Console.Write("1 ");if((val & t) == 0) Console.Write("0 ");}Вот как выглядит результат выполнения этой программы:01111011В цикле for с помощью поразрядного оператора И последовательно тестируетсякаждый бит переменной val.
Если оказывается, что этот бит установлен, отображаетсяцифра 1, в противном случае — цифра 0.Поразрядный оператор ИЛИ, в противоположность поразрядному И, удобноиспользовать для установки нужных битов в единицу. При выполнении операции ИЛИналичие в операнде бита, равного 1, означает, что в результате соответствующий бит такжебудет равен единице. Вот пример:1101 00111010 1010| ________1111 1011С помощью поразрядного оператора ИЛИ рассмотренную выше программуполучения четных чисел легко превратить в программу получения нечетных чисел.// Использование поразрядного оператора ИЛИ для// "превращения" любого числа в нечетное.using System;class MakeOdd {public static void Main() {ushort num;ushort i;}}for(i = 1; i <= 10; i++) {num = i;Console.WriteLine("num: " + num);num = (ushort) (num | 1); // num | 0000 0001Console.WriteLine("num после установки младшего бита: " + num + "\n");}Глава 4.
Операторы93Результат выполнения этого варианта программы таков:num: 1num после установки младшего бита: 1num: 2num после установки младшего бита: 3num: 3num после установки младшего бита: 3num: 4num после установки младшего бита: 5num: 5num после установки младшего бита: 5num: бnum после установки младшего бита: 7num: 7num после установки младшего бита: 7num: 8num после установки младшего бита: 9num: 9num после установки младшего бита: 9num: 10num после установки младшего бита: 11Работа этой программы основана на выполнении поразрядной операции ИЛИ междукаждым числом, генерируемым в цикле for, и числом 1, которое в двоичном кодепредставляется как 0000 0001.
Таким образом, 1 — это значение, у которого установлентолько один младший разряд. Если это значение является одним из операндов операцииИЛИ, то результат выполнения этой операции совпадет со вторым операндом заисключением его младшего разряда, который станет равным единице (а все остальные приэтом не изменятся). Следовательно, любое четное число, “пройдя” через операцию ИЛИ,увеличится на единицу, т.е.
станет нечетным.Поразрядное исключающее ИЛИ (XOR) устанавливает в единицу бит результататолько в том случае, если соответствующие биты операндов отличаются один от другого,т.е. не равны. Вот пример:0111 11111011 1001^ ________1100 0110Оператор XOR обладает одним интересным свойством, которое позволяетиспользовать его для кодирования сообщений. Если выполнить операцию XOR междузначением X и значением Y, а затем снова выполнить операцию XOR между результатомпервой операции и тем же значением Y, получим исходное значение X. Это значит, чтопосле выполнения двух операцийR1 = X ^ Y;R2 = R1 ^ Y;значение R2 совпадет со значением X. Таким образом, в результате выполнения двухпоследовательных операций XOR, использующих одно и то же значение (Y), получаетсяисходное значение (X). Этот принцип можно использовать для создания простой94Часть I. Язык C#программы шифрования, в которой некоторое целочисленное значение — ключ — служитдля кодирования и декодирования сообщения, состоящего из символов.
Для шифрованиясообщения операция исключающего ИЛИ применяется первый раз, а для его дешифровки— второй. Реализуем этот простой способ шифрования в следующей программе:// Использование оператора XOR для шифрования//и дешифрирования сообщения.using System;class Encode {public static void Main() {char ch1 = 'H';char ch2 = 'i';char ch3 = '!';int key = 88;}}Console.WriteLine("Исходное сообщение: " +ch1 + ch2 + ch3);// Шифруем сообщение.ch1 = (char) (ch1 ^ key);ch2 = (char) (ch2 ^ key);ch3 = (char) (ch3 ^ key);Console.WriteLine("Зашифрованное сообщение: " +ch1 + ch2 + ch3);// Дешифрируем сообщение.ch1 = (char) (ch1 ^ key);ch2 = (char) (ch2 ^ key);ch3 = (char) (ch3 ^ key);Console.WriteLine("Дешифрованное сообщение: " +ch1 + ch2 + ch3);Вот как выглядит результат выполнения этой программы:Исходное сообщение: Hi!Зашифрованное сообщение: >1уДешифрованное сообщение: Hi!Как видите, в результате выполнения двух операций XOR, использующих одно и тоже значение ключа, получается исходное (дешифрованное) сообщение.Унарный оператор НЕ (или оператор дополнения до 1) инвертирует состояние всехбитов своего операнда.
Например, если целочисленное значение (хранимое в переменнойА), представляет собой двоичный код 1001 0110, то в результате операции ~а получимдвоичный код 0110 1001.В следующей программе демонстрируется использование оператора НЕ посредствомотображения некоторого числа и его дополнения до 1 в двоичном коде.// Демонстрация поразрядного оператора НЕ.using System;class NotDemo {public static void Main() {Глава 4. Операторы95sbyte b = -34;int t;for(t = 128; t > 0; t = t/2) {if((b & t) != 0) Console.Write("1 ");if((b & t) == 0) Console.Write("0 "); }Console.WriteLine();// Инвертируем все биты.b = (sbyte) ~b;}}for(t = 128; t > 0; t = t/2) {if((b & t) != 0) Console.Write("1 ");if((b & t) == 0) Console.Write("0 ");}Выполнение этой программы дает такой результат:1101111000100001Операторы сдвигаВ C# можно сдвигать значение влево или вправо на заданное число разрядов.
Для этов C# определены следующие операторы поразрядного сдвига:<<сдвиг влево;>>сдвиг вправо.Общий формат записи этих операторов такой:значение << число_битов;значение >> число_битов.Здесь значение — это объект операции сдвига, а элемент число_битов указывает,на сколько разрядов должно быть сдвинуто значение.При сдвиге влево на один разряд все биты, составляющее значение, сдвигаются влевона одну позицию, а в младший разряд записывается нуль. При сдвиге вправо все битысдвигаются, соответственно, вправо.
Если сдвигу вправо подвергается значение без знака, встарший разряд записывается нуль. Если же сдвигу вправо подвергается значение сознаком, значение знакового разряда сохраняется. Вспомните: отрицательные целые числапредставляются установкой старшего разряда числа равным единице. Таким образом, еслисдвигаемое значение отрицательно, при каждом сдвиге вправо в старший разрядзаписывается единица, а если положительно — нуль.При сдвиге как вправо, так и влево крайние биты теряются.
Следовательно, при этомвыполняется нециклический сдвиг, и содержимое потерянного бита узнать невозможно.Ниже приводится программа, которая наглядно иллюстрирует результат сдвиговвлево и вправо. Значение, которое будет сдвигаться, устанавливается сначала равнымединице, т.е. только младший разряд этого значения “на старте” равен 1, все остальныеравны 0. После выполнения каждого из восьми сдвигов влево программа отображаетмладшие восемь разрядов нашего “подопытного” значения. Затем описанный процессповторяется, но в зеркальном отображении. На этот раз перед началом сдвига в переменнуюval заносится не 1, а 128, что в двоичном коде представляется как 1000 0000. И, конечноже, теперь сдвиг выполняется не влево, а вправо.96Часть I. Язык C#// Демонстрация использования операторов сдвига << и >>.using System;class ShiftDemo {public static void Main() {int val = 1;int t;int i;for(i = 0; i < 8; i++) {for(t = 128; t > 0; t = t/2) {if((val & t) != 0) Console.Write("1 ");if((val & t) == 0) Console.Write("0 ");}Console.WriteLine();val = val << 1; // Сдвиг влево.}Console.WriteLine();val = 128;for(i = 0; i < 8; i++) {for(t = 128; t > 0; t = t/2) {if((val & t) != 0) Console.Write("1 ");if((val & t) == 0) Console.Write("0 ");}Console.WriteLine();val = val >> 1; // Сдвиг вправо.}}}Вот результаты выполнения этой программы:0 0 0 0 0 0 0 10 0 0 0 0 0 1 00 0 0 0 0 1 0 00 0 0 0 1 0 0 00 0 0 1 0 0 0 00 0 1 0 0 0 0 00 1 0 0 0 0 0 01 0 0 0 0 0 0 010000000010000000 0 0 0 0 00 0 0 0 0 01 0 0 0 0 00 1 0 0 0 00 0 1 0 0 00 0 0 1 0 00 0 0 0 1 00 0 0 0 0 1Поскольку разряды представления двоичных чисел представляют собой степеничисла 2, то операторы сдвига можно использовать в качестве быстрого способа умноженияили деления чисел на 2.