Г. Шилдт - Полный справочник по C# (1160789), страница 20
Текст из файла (страница 20)
Например, если целочисленное значение (хранимое в переменной А), представляет собой двоичный код 1001 ОНО, то в результате операции~А получим двоичный код 0110 1001.В следующей программе демонстрируется использование оператора НЕ посредством отображения некоторого числа и его дополнения до 1 в двоичном коде.// Демонстрация поразрядного оператора НЕ.u s i n g System;c l a s s NotDemo {p u b l i c s t a t i c void Main() {Глава 4. Операторыr95sbyte 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 " ) ;Выполнение этой программы дает такой результат:1 1 0 1 1 1 1 00 0 1 0 0 0 0 1Операторы сдвигаВ С# можно сдвигать значение влево или вправо на заданное число разрядов. Дляэто в С# определены следующие операторы поразрядного сдвига:«сдвиг влево;»сдвиг вправо.Общий формат записи этих операторов такой:значение «значение »число битов;число_битов.Здесь значение — это объект операции сдвига, а элемент число_битов указывает,на сколько разрядов должно быть сдвинуто значение.При сдвиге влево на один разряд все биты, составляющее значение, сдвигаютсявлево на одну позицию, а в младший разряд записывается нуль.
При сдвиге вправовсе биты сдвигаются, соответственно, вправо. Если сдвигу вправо подвергается значение без знака, в старший разряд записывается нуль. Если же сдвигу вправо подвергается значение со знаком, значение знакового разряда сохраняется. Вспомните: отрицательные целые числа представляются установкой старшего разряда числа равнымединице. Таким образом, если сдвигаемое значение отрицательно, при каждом сдвигевправо в старший разряд записывается единица, а если положительно — нуль.При сдвиге как вправо, так и влево крайние биты теряются. Следовательно, приэтом выполняется нециклический сдвиг, и содержимое потерянного бита узнать невозможно.Ниже приводится программа, которая наглядно иллюстрирует результат сдвиговвлево и вправо. Значение, которое будет сдвигаться, устанавливается сначала равнымединице, т.е.
только младший разряд этого значения "на старте" равен 1, все остальные равны 0. После выполнения каждого из восьми сдвигов влево программа отображает младшие восемь разрядов нашего "подопытного" значения. Затем описанныйпроцесс повторяется, но в зеркальном отображении. На этот раз перед началом сдвигав переменную v a l заносится не 1, а 128, что в двоичном коде представляется как1000 0000. И, конечно же, теперь сдвиг выполняется не влево, а вправо.96Часть I. Язык С#// Демонстрация использования операторов сдвига «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;for(t=128; t0; t = t/2) {i = 0) Console.Write("1 " ) ;if((val & t) !=if((val & t) == 0) Console.Write("0 " ) ;Console.WriteLine();val = val >> 1; // Сдвиг вправо.0000000110000000Вот результаты выполнения этой программы:0 0 0 0 0 0 10 0 0 0 0 1 00 0 0 0 1 0 00 0 0 1 0 0 00 0 1 0 0 0 00 1 0 0 0 0 01 0 0 0 0 0 00 0 0 0 0 0 001000000001000000001000000001000000001000000001000000001Поскольку разряды представления двоичных чисел представляют собой степеничисла 2, то операторы сдвига можно использовать в качестве быстрого способа умножения или деления чисел на 2.
При сдвиге влево число удваивается. При сдвиге вправо число делится пополам. Конечно же, это будет справедливо до тех пор, пока с одного или другого конца не выдвинутся (и потеряются) значимые биты. Вот пример:// Использование операторов сдвига для// умножения и деления на 2.IГлава 4. Операторы97using System;class MultDiv {public 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 = 10;Console.WriteLine("Значение переменной n: " + n ) ;// Умножаем на 2, причем 30 раз.n = n << 30; // Увы: данные потеряны.Console.WriteLine("Значение п после сдвига влево на 30 разрядов: " + п ) ;Вот как выглядят результаты выполненияЗначение переменной п: 10Значение переменной п после п - п *Значение переменной п после n = n *Значение переменной п после n = n /Значение переменной п после n = n /этой программы:2:4:2:4:20804010Значение переменной п: 10Значение п после сдвига влево на 30 разрядов: -2147483648Обратите внимание на последнюю строку результатов выполнения программы.После сдвига числа 10 влево на 30 разрядов (т.е.
после умножения на 230) информациябудет потеряна, поскольку значение исходного числа было "выдвинуто" за пределыдиапазона представления чисел, соответствующего типу i n t . В данном случае вы видите странное отрицательное значение, которое получилось в результате попаданияДОЧасть I. Язык С#единицы в старший разряд числа, который для типа i n t используется в Качестве знакового. Вследствие этого число стало интерпретироваться как отрицательное. Этотпример показывает, как необходима осторожность при использовании операторовсдвига для умножения или деления чисел на 2.
(Чтобы вспомнить, чем отличаетсяпредставление значений со знаком от представления значений без знака, обратитесь кглаве 3.)Поразрядные составные операторы присваиванияВсе бинарные поразрядные операторы можно успешно объединять с операторомприсваивания, образуя поразрядные составные операторы присваивания. Например,следующие две инструкции присваивают переменной х результат выполнения операции исключающего ИЛИ (XOR) с операндами х и 127.1 х = х А 127;I х А = 127;ОператорОдним из самых замечательных операторов С# является тернарный оператор ?.Оператор ? часто используется для замены определенных типов конструкций ift h e n - e l s e . Оператор ? называется тернарным, поскольку он работает с тремя операторами. Его общий формат записи имеет такой вид:Выражение 1 ? Выражение2:Выражение3;Здесь Выражение1 должно иметь тип bool.
Типы элементов Выражение2 и Выражение 3 должны быть одинаковы. Обратите внимание на использование и расположение двоеточия.Значение ?-выражения определяется следующим образом. Вычисляется Выражение!. Если оно оказывается истинным, вычисляется Выражение2, и результат его вычисления становится значением всего ?-выражения.
Если результат вычисления элемента Выражение1 оказывается ложным, значением всего ?-выражения становитсярезультат вычисления элемента ВыражениеЗ. Рассмотрим пример, в котором переменной absval присваивается абсолютное значение переменной val.absval = v a l < 0 ? - v a l : v a l ; // Получаем абсолютное// значение v a l .Здесь переменной absval присваивается значение переменной v a l , если онобольше или равно нулю. Если же значение переменной v a l отрицательно, переменной absval присваивается результат применения к ней операции "унарный минус",который будет представлять собой положительное значение.Вот еще один пример использования оператора ?.
В следующей программе выполняется деление числа 100 на разные числа, но попытка деления на нуль реализованане будет.// Способ обойти деление на нуль с помощью оператора ?.Iusing System;class NoZeroDiv {public s t a t i c void Main() {int result;int i;Глава 4. Операторы99for{i = -5; i < 6;result = i != 0 ? 100 / i : 0;i f ( i != 0)Console.WriteLine("100 / " + i + " равно " + r e s u l t ) ;Посмотрите на результаты выполнения этой программы.100 / -5 равно -20100 / -4 равно -25100 / -3 равно -33100 / -2 равно -50100 / -1 равно -100100 / 1 равно 100100 / 2 равно 50100 / 3 равно 33100 / 4 равно 25100 / 5 равно 20Обратите внимание на следующую строку из этой программы:r e s u l t = i != 0 ? 100 / i : 0;Здесь переменной r e s u l t присваивается результат деления числа 100 на значениепеременной i.
Однако это деление выполнится только в том случае, если i не равнонулю. В противном случае (при i = 0) переменной r e s u l t будет присвоено нулевоезначение.В действительности совсем не обязательно присваивать переменной значение, генерируемое оператором ?. Например, вы могли бы использовать это значение в качестве аргумента, передаваемого методу.
Или возможен еще такой вариант. Если всевыражения, принимаемые оператором ?, имеют тип bool, то результат выполненияэтого оператора можно использовать в качестве условного выражения в цикле илиинструкции if. Рассмотрим, например, предыдущую программу, переписанную в более эффективном виде (результат ее выполнения аналогичен предыдущему).// Способ обойти деление на нуль с помощью ?-оператора.using System;class NoZeroDiv2 {public s t a t i c void Main() {int i ;for(i = -5; i < 6;i f ( i != 0 ? true : false)Console.WriteLine("100/ " +i +11равно " + 100 / i) ;Обратите внимание на инструкцию if.
Если значение переменной i равно нулю,результат проверки if-условия будет равен значению f a l s e , которое не допустит выполнения инструкции вывода, а значит, и деления на нуль. В противном случае деление (с выводом результата) будет иметь место.100Часть I. Язык С#Использование пробелов и круглых скобок||Любое выражение в С# для повышения читабельности может включать пробелы(или символы табуляции). Например, следующие два выражения совершенно одинаковы, но второе прочитать гораздо легче:х=10/у*(127/х);х = 10 / у *{121Ы);Круглые скобки (так же, как в алгебре) повышают приоритет операций, содержащихся внутри них. Использование избыточных или дополнительных круглых скобокне приведет к ошибке или замедлению вычисления выражения.