Тверской государственный университет
Опубликован: 22.11.2005 | Доступ: свободный | Студентов: 30404 / 1848 | Оценка: 4.31 / 3.69 | Длительность: 28:26:00
ISBN: 978-5-9556-0050-5
Лекция 6:

Выражения. Операции в выражениях

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >

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

Начну с предупреждения тем, кто привык к языку C++. Правила работы с логическими выражениями в языках C# и C++ имеют принципиальные различия. В языке C++ практически для всех типов существует неявное преобразование в логический тип. Правило преобразования простое, - ненулевые значения трактуются как истина, нулевое - как ложь. В языке C# неявных преобразований к логическому типу нет даже для целых арифметических типов. Поэтому вполне корректная в языке C++ запись:

int k1 = 7;
if (k1) Console.WriteLine("ok!");

незаконна в программах на C#. На этапе трансляции возникнет ошибка, поскольку вычисляемое условие имеет тип int, а неявное преобразование этого типа к типу bool отсутствует.

В языке C# более строгие правила действуют и для логических операций. Так, запись

if(k1 && (x>y)),

корректная в языке C++, приводит к ошибке в программах на C#, поскольку операция && определена только для операндов типа bool, а в данном выражении один из операндов имеет тип int. В языке C# в данных ситуациях следует использовать записи:

if(k1!=0)
if((k1>0) && (x>y))

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

/// <summary>
/// Логические выражения
/// </summary>
public void Logic()
{
	//операции отрицания ~,!
	bool b1,b2;
	b1 = 2*2==4; b2 =!b1;
	//b2= ~b1;
	uint j1 =7, j2;
	j2= ~j1;
	//j2 = !j1;
	int j4 = 7, j5;
	j5 = ~j4;
	Console.WriteLine("uint j2 = " + j2 +" int j5 = " + j5);
}//Logic

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

uint j2 = 4294967288 int j5 = -8

Бинарные логические операции " && - условное И " и " || - условное ИЛИ " определены только над данными типа bool. Операции называются условными или краткими, поскольку, будет ли вычисляться второй операнд, зависит от уже вычисленного значения первого операнда. В операции " && ", если первый операнд равен значению false, то второй операнд не вычисляется и результат операции равен false. Аналогично, в операции " || ", если первый операнд равен значению true, то второй операнд не вычисляется и результат операции равен true. Ценность условных логических операций заключается не в их эффективности по времени выполнения. Часто они позволяют вычислить логическое выражение, имеющее смысл, но в котором второй операнд не определен. Приведу в качестве примера классическую задачу поиска по образцу в массиве, когда разыскивается элемент с заданным значением (образец). Такой элемент в массиве может быть, а может и не быть. Вот типичное решение этой задачи в виде упрощенном, но передающем суть дела:

//Условное And - &&
int[] ar= {1,2,3};
int search = 7; int i=0;
while ((i < ar.Length) && (ar[i]!= search)) i++;
if(i<ar.Length) Console.WriteLine("Образец найден");
else Console.WriteLine("Образец не найден");

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

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

//Логические побитовые операции And, Or, XOR (&,|,^)
int k2 = 7, k3 = 5, k4, k5, k6;
k4 = k2 & k3; k5 = k2| k3; k6 = k2^k3;
Console.WriteLine("k4 = " + k4 + " k5 = " + k5 + " k6 = " + k6);

Приведу результаты вывода:

k4 = 5 k5 = 7 k6 =2

Приведу пример поиска по образцу с использованием логического AND:

i=0; search = ar[ar.Length - 1];
while ((i < ar.Length) & (ar[i]!= search)) i++;
if(i<ar.Length) Console.WriteLine("Образец найден");
else Console.WriteLine("Образец не найден");

В данном фрагменте гарантируется наличие образца поиска в массиве, и фрагмент будет успешно выполнен. В тех же случаях, когда массив не содержит элемента search, будет выброшено исключение. Содержательный смысл такой процедуры - появление исключения - может быть признаком ошибки в данных, что требует специальной обработки ситуации.

Условное выражение

В C#, как и в C++, разрешены условные выражения. Конечно, без них можно обойтись, заменив их условным оператором. Вот простой пример их использования, поясняющий синтаксис их записи:

//Условное выражение
int a = 7, b= 9, max;
max= (a>b) ? a:b;
Console.WriteLine("a = " + a + "; b= " + b +
	"; max(a,b) = " + max);

Условное выражение начинается с условия, заключенного в круглые скобки, после которого следует знак вопроса и пара выражений, разделенных двоеточием " : ". Условием является выражение типа bool. Если оно истинно, то из пары выражений выбирается первое, в противном случае результатом является значение второго выражения. В данном примере переменная max получит значение 9.

Операция приведения к типу

Осталось рассмотреть еще одну операцию - приведение к типу. Эта операция первого приоритета имеет следующий синтаксис:

(type) <унарное выражение>

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

//cast
int p;
double x=3.14159;
p = (int)x;
//b = (bool)x;

В данном примере явное преобразование из типа double в тип int выполняется, а преобразование double в тип bool приводит к ошибке, потому и закомментировано.

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >
Александр Галабудник
Александр Галабудник

Не обнаружил проекты, которые используются в примерах в лекции, также не увидел список задач.

Александра Гусева
Александра Гусева