Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 23
Текст из файла (страница 23)
Этим свойством данной операции можио воспользоваться для написания простой программы шифрования, в которой некоторое целое значение служит в качестве ключа для кодирования и декодирования сообщения с помощью операции исключающее ИЛИ иад символами этого сообщения. В первый раз операция исключающее ИЛИ выполняется для кодирования открытого текста в зашифрованный, а второй раз — для декодироваиия зашифрованного текста в открытый. Разумеется, такое шифрование не представляет никакой практической ценности, поскольку оио может быть легко разгадано.
Тем ие менее оно служит интересным примером для демонстрации результатов применения поразрядиых операторов исключающее ИЛИ, как в приведенной ниже программе. // Продемонстрировать применение поразрядного // оператора исключающее Иди. пзьпд зузпещ) с1аза Епсобе ( аеасьс чоьб Мауп() ( сваг сь1 = 'Н'; сваг сь2 = '1'; сваг спз = '!'; 1пт Хеу = 88; Сопао1е.вгьгевьпе("Исходное сообщение: " + сь1 + сь2 + свз); глава 4. операторы 109 // Зашифровать сообщение. сЬ1 = (сйаг) (сЬ1 " Кеу)т сй2 = (сйаг) (сЬ2 " Кеу); сйз = (сйаг) (сЬЗ " Кеу); Сопво1е.Игтгегфпе("Зашифрованное сообщение: " + сй1 + сЬ2 + сйз); // Расшифровать сообщение.
сй1 = (сйаг) (сЬ1 " Кеу): сй2 = (сйаг) (сЬ2 " Кеу); сйз = (сйаг) (сйз " Кеу); Сопяо1е.Игагетапе("Расшифрованное сообщение." сйт + сЬ2 + сйЗ)) Ниже приведен результат выполнения этой программы. Исходное сообщение: Исходное сообщение! Зашифрованное сообщение: 121у Расшифрованное сообщение: Н1! Как видите, в результате выполнения двух последовательностей поразрядных операций исключающее ИЛИ получается расшифрованное сообщение. (Еще раз напомним, что такое шифрование не имеет никакой практической ценности, поскольку оно, в сущности, ненадежно.) Поразрядный унарный оператор НЕ (или оператор дополнения до 1) изменяет на обратное состояние всех двоичных разрядов операнда.
Так, если некоторое целое значение А имеет комбинацию двоичных разрядов 1001 0110, то в результате поразрядной операции -й получается значение с комбинацией двоичных разрядов 0110 1001. В следующем примере программы демонстрируется применение поразрядного оператора НЕ с выводом некоторого числа и его дополнения до 1 в двоичном коде. // Продемонстрировать применение поразрядного // унарного оператора НК.
пяапч зуягеш) с1аяя Ногбешо ( ягаС1с гоаб Иатп() ( яЬуге Ь = -34) Рог(упг С=128т С > От С = С/2) ( 11((Ь Е С) != 0) Сопяо1е.иг1Се("1 ")( 1Р((ь а с) == 0) сопяо1е.иг1се("0 ")т ) Сопяо1е.нг1Се11пе()т // обратить все биты Ь = (яЬуге) -Ь; Рог(упс с=128; с > От с = с/2) ( 11((Ь а С) != 0) Сопяо1е.Иг1Се("1 ") 11((Ь ь С) == 0) Сопяо1е.игуге("0 ") ) 110 Часть (. Язык С№ Результат выполнения этой программы приведен ниже. 11О1111О Оа)аааат Операторы сдвига В С№ имеется возможность сдвигать двоичные разряды, составляющие целое значение, влево или вправо на заданную величину. Для этой цели в С№ определены два приведенных ниже оператора сдвига двоичных разрядов.
Сдвиг влево Сдвиг вправо « » Ниже приведена обшая форма для этих операторов. значение « число битов значение » число битов О Продемонстрировать применение операторов сдвига. озьпд Зузгекн с1авв ЗЬ1ГСПеио ( вгаггс чоьб Ма1л() ( гпк ча1 = 1; Гог(1пс 1 = Ок 1 < 8; 1~+) ( Гог(1пс к=128к с > 01 С = к/2) ( где число битов — это число двоичных разрядов, на которое сдвигается указанное значение.
При сдвиге влево все двоичные разряды в указываемом значении сдвигаются на одну позицию влево, а младший разряд сбрасывается в нуль. При сдвиге вправо все двоичные разряды в указываемом значении сдвигаются на одну позицию вправо. Если вправо сдвигается целое значение без знака, то старший разряд сбрасывается в нуль. А если вправо сдвигается целое значение со знаком, то разряд знака сохраняется. Напомним, что для представления отрицательных чисел старший разряд целого числа устанавливается в 1.
Так, если сдвигаемое значение является отрицательным, то при каждом сдвиге вправо старший разряд числа устанавливается в 1. А если сдвигаемое значение является положительным, то при каждом сдвиге вправо старший разряд числа сбрасывается в нуль. При сдвиге влево и вправо крайние двоичные разряды теряются.
Восстановить потерянные при сдвиге двоичные разряды нельзя, поскольку сдвиг в данном случае не является циклическим. Ниже приведен пример программы, наглядно демонстрирующий действие сдвига влево и вправо. В данном примере сначала задается первоначальное целое значение, равное 1. Это означает, что младший разряд этого значения установлен. Затем зто целое значение сдвигается восемь раз подряд влево.
После каждого сдвига выводятся восемь младших двоичных разрядов данного значения. Далее процесс повторяется, но на этот раз сначала задается целое значение 128, которое затем сдвигается восемь раз подряд вправо. Глава 4. Операторы 111 18((ча1 а г) (= 0) сопво1е.игтге("1 "); 11((ча1 а г) == 0] сопво1е.игьге("0 "); ) Сопао1е.нгасеьапе[); ча1 = ча1 « 1; // сдвиг влево Сопво1е.нгьсепьпе(); ча1 = 128) лог(апс 1 = Оу г < 8; 1а+) ( лог(апс Г=128у С > 0~ Г = Г/2) 11((ча1 а Г) (= 0) Сопво1е.нг1се("1 "); 11((ча1 а с) = О) Сопво1е.Игтсе('"0 "); ) Сопао1е.Игьсеььпе(); ча1 = ча1 » 1; // сдвиг вправо ) ) Результат выполнения этой программы выглядит следующим образом: 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000 10000000 01000000 00100000 00010000 00001000 00000100 00000010 00000001 Двоичные разряды соответствуют форме представления чисел в степени 2, и поэтому операторы сдвига могут быть использованы для умножения или деления целых значений на 2.
Так, при сдвиге вправо целое значение удваивается, а при сдвиге влево — уменьшается наполовину Разумеется, все это справедливо лишь в том случае, если крайние разряды не теряются при сдвиге в ту или иную сторону Ниже приведен соответствующий пример. // Применить операторы сдвига для умножения и // деления на 2. пв1пд зувсеи; с1аав Мо1гшч ( всасас чоги Матп() ( ьпг пу 112 часть (. язык Сз Сопво1е.Игтсеьцпе (" Значение переменной и: '" т и) т // Умножить на 2.
и = п « 17 Сопво1е.иг1сеЬ1пе("Значение переменной п после " + "операции и = п * 2: " + и)) // Умножить на 4. и=и«гт Соизо1е.игткевьие("Значение переменной и после " + "операции п = п * 4: " + п)т // Разделить на 2. п = п» 1) Сопзо1е.иг1геь1пе("Зцачение переменной п после " + "операции п = п / 2: " + п)т // Разделить на 4.
п = и» 27 Сопво1е.игтгеььпе("Значение переменной п после " + "операции и = п / 4: " + п)) Соиво1е.иг1сеььие()) // Установить переменную и в исходное состояние. и = 10; Сопзо1е.нгтсеьтпе("Значение переменной и: '" 4 и)7 // Умножить на 2 тридцать раз. п = и « 307 // данные теряются Сопзо1е.игггеьтпе("Значение переменной п после " + "сдвига на 30 позиций влево: " + п); Ниже приведен результат выполнения этой программы. Значение переменной п: 10 Значение переменной п после операции п = п * 2т 20 Значение переменной п после операции п = п * 4: 80 Значение переменной п после операции и = п / 2: 40 Значение переменной п после операции и = п / 4: 10 Значение переменной и: 10 Значение переменной и после сдвига на 30 позиций влево: -2147483648 Обратите внимание на последнюю строку приведенного выше результата.
Когда целое значение 10 сдвигается влево тридцать раз подряд, информация теряется, поскольку двоичные разряды сдвигаются за пределы представления чисел для типа 1ис. В данном случае получается совершенно непригодное значение, которое оказывается к тому же отрицательным, поскольку в результате сдвига в старшем разряде, используемом в качестве знакового, оказывается 1, а следовательно, данное числовое значение должно интерпретироваться как отрицательное.
Этот пример наглядно показывает, что применять операторы сдвига для умножения или деления на 2 следует очень аккуратно. (Подробнее о типах данных со знаком и без знака см. в главе 3.) Глава 4. Операторы 113 Поразрядные составные операторы присваивания Все двоичные поразрядные операторы могут быть использованы в составных операциях присваивания. Например, в двух приведенных ниже операторах переменной х присваивается результат выполнения операции исключающее ИЛИ над первоначальным значением переменной х и числовым значением 127. к = к " 1277 х "= 127; Оператор ? Оператор? относится к числу самых примечательных в С№.
Он представляет собой условный оператор и часто используется вместо определенных видов конструкций г 5 — г пел -е1эе. Оператор? иногда еще называют тернарным, поскольку для него требуются три операнда. Ниже приведена общая форма этого оператора. Выражение1 ? Выражение2 з Выражениеэ? Здесь Выражение1 должно относиться к типу Ьоо1, а Выражение2 и ВыражениеЗ— к одному и тому же типу. Обратите внимание на применение двоеточия и его местоположение в операторе?.
Значение выражения '. определяется следующим образом. Сначала вычисляется Выражение1. Если оно истинно, то вычисляется Выражение2, а полученный результат определяет значение всего выражения '. в целом. Если же выражение1 оказывается ложным, то вычисляется Выражение 3, и его значение становится общим для всего выражения ?. Рассмотрим следующий пример, в котором переменной аЬэуа1 присваивается значение переменной уа1. апвуа1 = уа1 < 0 ? -на1 : на17 // получить абсолютное // значение переменной ча1 В данном примере переменной аЬэча1 присваивается значение переменной на1, если оно больше или равно нулю.
Если же значение переменной ча1 отрицательно, то переменной аЬэуа1 присваивается результат отрицания этого значения, что в итоге дает положительное значение. Ниже приведен еще один пример применения оператора?. В данной программе одно число делится на другое, но при этом исключается деление на нуль. // исключить деление на нуль, используя оператор?. пвьпд эувгеюз с1авз Мозегопту ( зсаг1с чета Иаьп() ( ьпс гевч1Г? лог(ьпг 1 = -57 1 < бз 1++) ( гевп1Г = 1 != 0 ? 100 / г: 0; 1г(з.
!= 0) Сопво1е.нгтсеЬ1пе("100 / " + з + " равно " т гевп1Г); ) ) ) 114 Часть ). Язык С№ Выполнение этой программы дает следующий результат: 100 / -5 равно -20 100 / -4 равно -25 100 / -3 равно -33 100 / -2 равно -50 100 / -1 равно -100 100 / 1 равно 100 100 / 2 равно 50 100 / 3 равно 33 100 / 4 равно 25 100 / 5 равно 20 Обратите особое внимание на следующую строку из приведенной ь..ше программы: гевп1С = 1 )= 0 2 100 / 1: О) В этой строке переменной гезп10 присваивается результат деления числа 100 на значение переменной 1. Но это деление осушествляется лишь в том случае, если значение переменной 1 не равно нулю. Когда же оно равно нулю, переменной гезп11 присваивается значение, обнуляющее результат. Присваивать переменной результат выполнения оператора '. совсем не обязательно.
Например, значение, которое дает оператор 2, можно использовать в качестве аргумента при вызове метода. А если все выражения в операторе '. относятся к типу )ооо1, то такой оператор может заменить собой условное выражение в цикле или операторе 11. В приведенном ниже примере программы выводятся результаты деления числа 100 только на четные, ненулевые значения. // Разделить только на четные, ненулевые значения. ивапч Зувкевт о1авв Ноэеговач2 ( акак1с чо10 Маап 0 ( Гог(апк 1 = -54 1 < бт ать) ь№(1 (= 0 2 (ь№2 == 0) : Га1ве) Сопао1е.ыг1кеЬ1пе("100 / " + 1 + " равно " + 100 / 1); Обратите внимание на оператор 11 в приведенной выше программе.
Если значение переменной 1 равно нулю, то оператор 11 дает ложный результат. А если значение переменной 1 не равно нулю, то оператор 11 дает истинный результат, когда значение переменной 1 оказывается четным, и ложный результат, если оно нечетное. Благодаря этому допускается деление только на четные и ненулевые значения. Несмотря на то что данный пример служит лишь для целей демонстрации, подобные конструкции иногда оказываются весьма полезными. Использование пробелов и круглых скобок В выражении на С№ допускается наличие символов табуляции и пробелов, благодаря которым оно становится более удобным для чтения. Например, оба приведенных ниже выражения, по существу, одинаковы, но второе читается легче. Глава 4.