Добавить в цитаты Настройки чтения

Страница 22 из 188

У поразрядного оператора исключающее ИЛИ имеется одно интересное свойство,которое оказывается полезным в самых разных ситуациях. Так, если выполнить сначала поразрядную операцию исключающее ИЛИ одного значения X с другим значениемY, а затем такую же операцию над результатом предыдущей операции и значением Y,то вновь получится первоначальное значение X. Это означает, что в приведенном нижефрагменте кодаR1 = X ^ Y;R2 = R1 ^ Y;

значение переменной R2 оказывается в итоге таким же, как и значение переменной X.Следовательно, в результате двух последовательно выполняемых поразрядных операций исключающее ИЛИ, в которых используется одно и то же значение, подучаетсяпервоначальное значение. Этим свойством данной операции можно воспользоватьсядля написания простой программы шифрования, в которой некоторое целое значениеслужит в качестве ключа для кодирования и декодирования сообщения с помощьюоперации исключающее ИЛИ над символами этого сообщения. В первый раз операция исключающее ИЛИ выполняется для кодирования открытого текста в зашифрованный, а второй раз — для декодирования зашифрованного текста в открытый. Разумеется, такое шифрование не представляет никакой практической ценности, поскольку оно может быть легко разгадано. Тем не менее оно служит интересным примеромдля демонстрации результатов применения поразрядных операторов исключающееИЛИ, как в приведенной ниже программе.// Продемонстрировать применение поразрядного оператора исключающее ИЛИ.using System;class Encode { 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!Зашифрованное сообщение: □1yРасшифрованное сообщение: Hi!

Как видите, в результате выполнения двух последовательностей поразрядных операций исключающее ИЛИ получается расшифрованное сообщение. (Еще раз напомним, что такое шифрование не имеет никакой практической ценности, поскольку оно,в сущности, ненадежно.)

Поразрядный унарный оператор НЕ (или оператор дополнения до 1) изменяет наобратное состояние всех двоичных разрядов операнда. Так, если некоторое целое значение А имеет комбинацию двоичных разрядов 1001 0110, то в результате поразряднойоперации ~А получается значение с комбинацией двоичных разрядов 0110 1001.

В следующем примере программы демонстрируется применение поразрядногооператора НЕ с выводом некоторого числа и его дополнения до 1 в двоичном коде.// Продемонстрировать применение поразрядного унарного оператора НЕ.using System;class NotDemo { static void Main() { sbyte b = -34; for(int 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(int t=128; t > 0; t = t/2) { if((b & t) != 0) Console.Write("1 "); if((b & t) == 0) Console.Write("0 "); } }}

Результат выполнения этой программы приведен ниже.1 1 0 1 1 1 1 00 0 1 0 0 0 0 1Операторы сдвига

В C# имеется возможность сдвигать двоичные разряды, составляющие целое значение, влево или вправо на заданную величину. Для этой цели в C# определены дваприведенных ниже оператора сдвига двоичных разрядов.<<Сдвиг влево>>Сдвиг вправо

Ниже приведена общая форма для этих операторов:значение << число_битовзначение >> число_битов



где число_битов — это число двоичных разрядов, на которое сдвигается указанноезначение.

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

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

Ниже приведен пример программы, наглядно демонстрирующий действие сдвигавлево и вправо. В данном примере сначала задается первоначальное целое значение,равное 1. Это означает, что младший разряд этого значения установлен. Затем это целое значение сдвигается восемь раз подряд влево. После каждого сдвига выводятся восемь младших двоичных разрядов данного значения. Далее процесс повторяется, но наэтот раз 1 устанавливается на позиции восьмого разряда, а по существу, задается целоезначение 128, которое затем сдвигается восемь раз подряд вправо.// Продемонстрировать применение операторов сдвига.using System;class ShiftDemo { static void Main() { int val = 1; for(int i = 0; i < 8; i++) { for(int 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(int i = 0; i < 8; i++) { for(int 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 01 0 0 0 0 0 0 00 1 0 0 0 0 0 00 0 1 0 0 0 0 00 0 0 1 0 0 0 00 0 0 0 1 0 0 00 0 0 0 0 1 0 00 0 0 0 0 0 1 00 0 0 0 0 0 0 1

Двоичные разряды соответствуют форме представления чисел в степени 2, и поэтому операторы сдвига могут быть использованы для умножения или деления целыхзначений на 2. Так, при сдвиге вправо целое значение удваивается, а при сдвиге влево — уменьшается наполовину. Разумеется, все это справедливо лишь в том случае,если крайние разряды не теряются при сдвиге в ту или иную сторону. Ниже приведенсоответствующий пример.// Применить операторы сдвига для умножения и деления на 2.using System;class MultDiv { static void Main() { int n; n = 10; Console.WriteLine("Значение переменной n: " + n); // Умножить на 2. n = n << 1; Console.WriteLine("Значение переменной n после " + "операции n = n * 2: " + n); // Умножить на 4. n = n << 2; Console.WriteLine("Значение переменной n после " + "операции n = n * 4: " + n); // Разделить на 2. n = n >> 1; Console.WriteLine("Значение переменной n после " + "операции n = n / 2: " + n); // Разделить на 4. n = n >> 2; Console.WriteLine("Значение переменной n после " + "операции n = n / 4: " + n); Console.WriteLine(); // Установить переменную n в исходное состояние. n = 10; Console.WriteLine("Значение переменной n: " + n); // Умножить на 2 тридцать раз. n = n << 30; // данные теряются Console.WriteLine("Значение переменной п после " + "сдвига на 30 позиций влево: " + n); }}

Ниже приведен результат выполнения этой программы.Значение переменной n: 10Значение переменной n после операции n = n * 2: 20Значение переменной n после операции n = n * 4: 80Значение переменной n после операции n = n / 2: 40Значение переменной n после операции n = n / 4: 10Значение переменной n: 10Значение переменной n после сдвига на 30 позиций влево: -2147483648