Ассоциативность операторов




Как выше было отмечено, операции умножения и деления имеют один и тот же приоритет, но какой тогда результат будет в выражении:

  int x = 10 / 5 * 2;

Стоит нам трактовать это выражение как (10 / 5) * 2 или как 10 / (5 * 2)? Ведь в зависимости от трактовки мы получим разные результаты.

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

  • Левоассоциативные операторы, которые выполняются слева направо
  • Правоассоциативные операторы, которые выполняются справа налево

Все арифметические операторы (кроме префиксного инкремента и декремента) являются левоассоциативными, то есть выполняются слева направо. Поэтому выражение 10 / 5 * 2 необходимо трактовать как (10 / 5) * 2, то есть результатом будет 4.

Операции отношения

Операции отношения используются для сравнения значений переменных и констант. Всего имеется 6 операций отношения: == (равно),!= (не равно), < (меньше), > (больше), <= (меньше или равно), >= (больше или равно).

Следует обратить внимание на запись операции "равно", как == (два знака присвоить =), и "нс равно", как!=.

Также отметим, что при сравнении ссылочных переменных сравниваются не сами объекты, а ссылки на объекты (если операция сравнения не переопределена для данного типа).

Логические операции

В языке C# логические операции делятся на две категории: одни выполняются над логическими значениями операндов, другие осуществляют выполнение логической операции над битами операндов. По этой причине в C# существуют две унарные операции отрицания – логическое отрицание, заданное операцией!, и побитовое отрицание, заданное операцией ~. Первая из них определена над операндом типа bool, вторая – над операндом целочисленного типа, начиная с типа int и выше (int, uint, long, ulong). Результатом операции во втором случае является операнд, в котором каждый бит заменен его дополнением (0 на 1 и 1 на 0). Рассмотрим пример: bool b1, b2;

М = 2*2==4; // М = true;

b2 =!b1; // b2 = false;

//b2= ~b1; // ошибка! uint j1 =7, j2;

j2= ~j1; //j2 = 4294967288

//j2 =!j1; //ошибка! int j4 = 7, j5; j5 = ~j4; // j5 = -8

В этом фрагменте закомментированы операторы, приводящие к ошибкам. В первом случае была сделана попытка применения операции побитового отрицания к выражению типа bool, во втором – логическое отрицание применялось к целочисленным данным. И то и другое в C# незаконно. Обратите внимание на разную интерпретацию побитового отрицания для беззнаковых и знаковых целочисленных типов. Для переменных j5 и 2 строка битов, задающая значение, одна и та же, но интерпретируется по-разному.

Бинарные логические операции (&& – условное И и || – условное ИЛИ) определены только над данными типа bool. Операции называются условными, или краткими, поскольку будет ли вычисляться второй операнд, зависит от уже вычисленного значения первого операнда.

операции &&, если первый операнд равен значению false, второй операнд не вычисляется и результат операции равен false. Аналогично в операции ||: если первый операнд равен значению true, то второй операнд не вычисляется и результат операции равен true.

Ценность условных логических операций заключается не в их эффективности по времени выполнения. Часто они позволяют вычислить логическое выражение, имеющее смысл, но в котором второй операнд не определен. Например, рассмотрим задачу поиска элемента массива. Заданный элемент в массиве может быть, а может и не быть. В этом случае типичным решением данной задачи будет:

// Условное And – && int[ ] ar= {1,2,3};

int search = 7, i=0;

// search – заданное значение

while ((i < ar.Length) && (ar[i]!= search)) i++;

if(i < ar.Length)

Console.WriteLine("Значение найдено”); else

Console.WriteLine("Значение не найдено");

Если значение переменной search не совпадает ни с одним из значений элементов массива аг, то последняя проверка условия цикла while будет выполняться при значении i, равном ar.Length. В этом случае первый операнд получит значение false, и, хотя второй операнд при этом не определен, цикл нормально завершит свою работу. Второй операнд не определен в последней проверке, поскольку индекс элемента массива выходит за допустимые пределы (в C# индексация элементов начинается с нуля). Отметим, что "нормальная" конъюнкция требует вычисления обоих операндов, поэтому ее применение в данной программе приводило бы к формированию исключения в случае, когда образца нет в массиве.

Три бинарные побитовые операции: & – AND, | – OR, ^ – XOR могут использоваться как с целыми типами выше int, так и с булевыми типами. В первом случае они используются как побитовые операции, во втором – как обычные логические операции. Иногда необходимо, чтобы оба операнда вычислялись в любом случае, тогда без этих операций не обойтись. Вот пример первого их использования:

//Логические побитовые операции And, Or, XOR (&,|,^) int k2 = 7, k3 = 5, k4, k5, кб; k4 = k2 & k3; k5 = k2 | k3; к6 = к2лкЗ;

Оператор Операция Написание
& Побитовое умножение Поразрядно умножает два числа. Если значение обоих разрядов 1, то в результирующем разряде 1, иначе 0 a & b
| Побитовое сложение Поразрядно складывает два числа. Если значение хотя бы одного из разрядов 1, то в результирующем разряде 1, иначе 0 a | b
^ Побитовое сложение по модулю Поразрядно складывает по модулю два числа. Если значения обоих разрядов одинаковы, то в результирующем разряде 0, иначе 1 a ^ b
~ Побитовое отрицание Поразрядно инвертирует число. Если значение разряда 1, то в результирующем разряде 0, иначе 1 ~a
>> Сдвиг вправо Сдвигает левый операнд вправо на указанное справа количество двоичных разрядов. Крайние биты справа стираются, а слева приписываются нули, если тип числа не имеет знака (не может быть отрицательным), или знаковые биты, если тип числа знаковый. a>>b
<< Сдвиг влево Сдвигает левый операнд вправо на указанное справа количество двоичных разрядов. Крайние биты слева стираются, а биты справа заполняются нулями. a<<b

Особенности битовых операций:

  • Могут использоваться с целочисленными типами и перечислениями (enum).
  • Дважды последовательно выполнив сложение по модулю, будет возвращено изначальное число. Это широко применяется в шифровании.
  • Для использования операторов сдвига, правый операнд должен быть числом, не превышающим количество битов, занимаемого левым операндом.

Примеры:

byte t1 = 15; // 00001111 в двоичном представлении byte t2 = 21; // 00010101 в двоичном представлении byte t3 = t1&t2; // 5 в десятичном, или 00000101 в двоичном представлении byte t4 = t1|t2; // 31 в десятичном, или 00011111 в двоичном представлении byte t5 = t1^t2; // 26 в десятичном, или 00011010 в двоичном представлении byte t6 = ~(t1|t2); // 224 в десятичном, или 11100000 в двоичном представлении byte t7 = t5^t2; //15, то есть восстановлено значение t1 byte t8 = t1>>2; // 3 в десятичном, или 00000011 в двоичном представлении byte t9 = t2<<4; // 80 в десятичном, или 01010000 в двоичном представлении

6.Операция определения размера объекта (sizeof).

Позволяет получить размер в байтах для неуправляемого типа.

К неуправляемым типам относятся:

· Простые типы, которые перечислены в следующей таблице:

Выражение Константа
sizeof(sbyte)  
sizeof(byte)  
sizeof(short)  
sizeof(ushort)  
sizeof(int)  
sizeof(uint)  
sizeof(long)  
sizeof(ulong)  
sizeof(char) 2 (Юникод)
sizeof(float)  
sizeof(double)  
sizeof(decimal)  
sizeof(bool)  

· Типы перечисления.

· Типы указателей.

· Определяемые пользователем структуры, которые не содержат полей экземпляров или автоматически реализуемых свойств, являющихся ссылочными или сконструированными типами.

В приведенном ниже примере показано, как извлекать размер переменной типа int.

C#Копировать

// Constant value 4:int intSize = sizeof(int);

Примечания

Начиная с версии 2.0 языка C# применение sizeof к простым типам или типам перечисления больше не требует компиляции кода в небезопасном контексте.

Оператор sizeof перегрузить нельзя. Возвращаемые оператором sizeof значения принадлежат типу int. В предыдущей таблице показаны константы, которые заменяются выражениями sizeof, содержащими некоторые операнды простых типов.

Для всех остальных типов, в том числе для структур, оператор sizeof можно использовать только в блоках небезопасного кода. Несмотря на то, что вы можете использовать метод Marshal.SizeOf, возвращаемое им значение не всегда совпадает со значением, которое возвращает метод sizeof. Метод Marshal.SizeOf возвращает размер после маршалинга типа, тогда как оператор sizeof возвращает размер сразу после выделения памяти средой CLR, включая заполнения.

Пример

C#Копировать

class MainClass{ // unsafe not required for primitive types static void Main() { Console.WriteLine("The size of short is {0}.", sizeof(short)); Console.WriteLine("The size of int is {0}.", sizeof(int)); Console.WriteLine("The size of long is {0}.", sizeof(long)); }}/*Output: The size of short is 2. The size of int is 4. The size of long is 8.*/

 

7.Область видимости переменных.



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2019-06-03 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: