У составных операторов присваивания имеются два главных преимущества. Во- первых, они более компактны, чем их "несокращенные" эквиваленты. И во-вторых, они дают более эффективный исполняемый код, поскольку левый операнд этих операторов вычисляется только один раз. Именно по этим причинам составные операторы присва ивания чаще всего применяются в программах, профессионально написанных на С#. Поразрядные операторы
В C# предусмотрен ряд поразрядных операторов, расширяющих круг задач, для ре шения которых можно применять С#. Поразрядные операторы воздействуют на от дельные двоичные разряды (биты) своих операндов. Они определены только для цело численных операндов, поэтому их нельзя применять к данным типа bool, float или double.
Эти операторы называются поразрядными, поскольку они служат для проверки, установки или сдвига двоичных разрядов, составляющих целое значение. Среди про чего поразрядные операторы применяются для решения самых разных задач програм мирования на уровне системы, включая, например, анализ информации состояния устройства. Все доступные в C# поразрядные операторы приведены в табл. 4.1.
Таблица 4.1. Поразрядные операторы Оператор Значение & Поразрядное И | Поразрядное ИЛИ ^ Поразрядное исключающее ИЛИ > Сдвиг вправо << Сдвиг влево ~ Дополнение до 1 (унарный оператор НЕ) Поразрядные операторы И, ИЛИ, исключающее ИЛИ и НЕ
Поразрядные операторы И, ИЛИ, исключающее ИЛИ и НЕ обозначаются следую щим образом: &, |, ^ и ~. Они выполняют те же функции, что и их логические аналоги, рассмотренные выше. Но в отличие от логических операторов, поразрядные операто ры действуют на уровне отдельных двоичных разрядов. Ниже приведены результаты поразрядных операций с двоичными единицами и нулями. Р q р & q р | q р ^ q ~р 0 0 0 0 0 1 1 0 0 1 1 0 0 1 0 1 1 1 1 1 1 1 0 0
С точки зрения наиболее распространенного применения поразрядную операцию И можно рассматривать как способ подавления отдельных двоичных разрядов. Это означает, что если какой-нибудь бит в любом из операндов равен 0, то соответствую щий бит результата будет сброшен в 0. Например: 1101 0011 1010 1010 & __________ 1000 0010
В приведенном ниже примере программы демонстрируется применение пораз рядного оператора & для преобразования нечетных чисел в четные. Для этой цели достаточно сбросить младший разряд числа. Например, число 9 имеет следующий двоичный вид: 0000 1001. Если сбросить младший разряд этого числа, то оно станет числом 8, а в двоичной форме — 0000 1000. // Применить поразрядный оператор И, чтобы сделать число четным. using System; class MakeEven { static void Main() { ushort num; ushort i; for(i = 1; i <= 10; i++) { num = i; Console.WriteLine("num: " + num); num = (ushort) (num & 0xFFFE); Console.WriteLine("num после сброса младшего разряда: " + num + "\n"); } } }
Результат выполнения этой программы приведен ниже. num: 1 num после сброса младшего разряда: 0 num: 2 num после сброса младшего разряда: 2 num: 3 num после сброса младшего разряда: 2 num: 4 num после сброса младшего разряда: 4 num: 5 num после сброса младшего разряда: 4 num: 6 num после сброса младшего разряда: 6 num: 7 num после сброса младшего разряда: 6 num: 8 num после сброса младшего разряда: 8 num: 9 num после сброса младшего разряда: 8 num: 10 num после сброса младшего разряда: 10
Шестнадцатеричное значение 0xFFFE, используемое в поразрядном операторе И, имеет следующую двоичную форму: 1111 1111 1111 1110. Таким образом, пораз рядная операция И оставляет без изменения все двоичные разряды в числовом значе нии переменной num, кроме младшего разряда, который сбрасывается в нуль. В итоге четные числа не претерпевают никаких изменений, а нечетные уменьшаются на 1 и становятся четными.
Поразрядным оператором И удобно также пользоваться для определения уста новленного или сброшенного состояния отдельного двоичного разряда. В следующем примере программы определяется, является ли число нечетным. // Применить поразрядный оператор И, чтобы определить, // является ли число нечетным. using System; class IsOdd { static void Main() { ushort num; num = 10; if((num & 1) == 1) Console.WriteLine("He выводится."); num = 11; if((num & 1) == 1) Console.WriteLine(num + " — нечетное число."); } }
Вот как выглядит результат выполнения этой программы. 11 — нечетное число.
В обоих операторах if из приведенной выше программы выполняется поразрядная операция И над числовыми значениями переменной num и 1. Если младший двоич ный разряд числового значения переменной num установлен, т.е. содержит двоичную 1, то результат поразрядной операции num & 1 оказывается равным 1. В противном случае он равен нулю. Поэтому оператор if может быть выполнен успешно лишь в том случае, если проверяемое число оказывается нечетным.
Возможностью проверять состояние отдельных двоичных разрядов с помощью по разрядного оператора & можно воспользоваться для написания программы, в которой отдельные двоичные разряды проверяемого значения типа byte приводятся в двоич ной форме. Ниже показан один из способов написания такой программы. // Показать биты, составляющие байт. using System; class ShowBits { static void Main() { int t; byte val; val = 123; for (t=l28; t > 0; t = t/2) { if((val & t) != 0) Console.Write("1 "); if((val & t) == 0) Console.Write("0 "); } } }
Выполнение этой программы дает следующий результат. 0 1 1 1 1 0 1 1
В цикле for из приведенной выше программы каждый бит значения переменной val проверяется с помощью поразрядного оператора И, чтобы выяснить, установлен ли этот бит или сброшен. Если он установлен, то выводится цифра 1, а если сброшен, то выводится цифра 0.
Поразрядный оператор ИЛИ может быть использован для установки отдельных дво ичных разрядов. Если в 1 установлен какой-нибудь бит в любом из операндов этого опе ратора, то в 1 будет установлен и соответствующий бит в другом операнде. Например: 1101 0011 1010 1010 | __________ 1111 1011
Используя поразрядный оператор ИЛИ, можно без особого труда превратить упо минавшийся выше пример программы, преобразующей нечетные числа в четные, в приведенный ниже обратный пример, где четные числа преобразуются в нечетные. // Применить поразрядный оператор ИЛИ, чтобы сделать число нечетным. using System; class MakeOdd { static void Main() { ushort num; ushort i; for(i = 1; i <= 10; i++) { num = i; Console.WriteLine("num: " + num); num = (ushort) (num | 1); Console.WriteLine("num после установки младшего разряда: " + num + "\n"); } } }
Результат выполнения этой программы выглядит следующим образом. num: 1 num после установки младшего разряда: 1 num: 2 num после установки младшего разряда: 3 num: 3 num после установки младшего разряда: 3 num: 4 num после установки младшего разряда: 5 num: 5 num после установки младшего разряда: 5 num: 6 num после установки младшего разряда: 7 num: 7 num после установки младшего разряда: 7 num: 8 num после установки младшего разряда: 9 num: 9 num после установки младшего разряда: 9 num: 10 num после установки младшего разряда: 11
В приведенной выше программе выполняется поразрядная операция ИЛИ над каждым числовым значением переменной num и 1, поскольку 1 дает двоичное значе ние, в котором установлен младший разряд. В результате поразрядной операции ИЛИ над 1 и любым другим значением младший разряд последнего устанавливается, тогда как все остальные разряды остаются без изменения. Таким образом, результирующее числовое значение получается нечетным, если исходное значение было четным.
Поразрядный оператор исключающее ИЛИ устанавливает двоичный разряд опе ранда в том и только в том случае, если двоичные разряды сравниваемых операндов оказываются разными, как в приведенном ниже примере. 0111 1111 1011 1001 ^ __________ 1100 0110
У поразрядного оператора исключающее ИЛИ имеется одно интересное свойство, которое оказывается полезным в самых разных ситуациях. Так, если выполнить снача ла поразрядную операцию исключающее ИЛИ одного значения 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!