Логические выражения си. Условные выражения

 Синтаксис оператора присвоения языка СИ имеет вид:
  LValue = RValue;
  LValue – это то куда будет записано значение. В роли такого объекта в СИ может выступать только переменная.
 RValue – это то значение чего мы запишем в LValue. А в этой роли могут выступать такие объекты как:
  переменная,
  константа,
  оператор вызова функции,
  математическое или логическое выражение.
 Примеры присвоений
  int a, b, c;
  double x, y;
  a = 5; b = 4; c = a + b;
  x = 5.0; y = exp(x);

Усовершенствованные операторы присвоений в СИ

 В СИ присутствуют так называемые усовершенствованные операторы присвоения, выглядят они так:
  LValue X= RValue; где X – это одна операция из набора: + - * / % ^ & | >. это является аналогией оператора присвоения:
  LValue = LValue X RValue;
 К примеру:
  a += b; ≡ a = a + b;
 В языке СИ все математические операции можно разделить на 2 группы:
  1. математические операции для вещественных и целочисленных вычислений;
  2. математические операции только для целочисленных вычислений.

К математическим операциям для вещественных и целочисленных вычислений языка СИ относят обычные арифметические операции:
  сложения (+),
  вычитания (-),
  умножения (*),  деления (/).

Соответствие типа результата от типов операндов

Особенности языка СИ

 Рассмотрим одну из особенностей на примере:
  int a,b;
  double c;
  a = 10;
  b = 4;
  c = a / b; // c будет равно 2, т.к выполняется операция не деления, а деления нацело или же:
  double x = 1 / 3; // x будет равен 0, по той же причине что и в предыдущем примере

Операции для целочисленных вычислений

 К операциям целочисленных вычислений относятся:
  операция взятия остатка от деления,
  побитовые операции,
  операции сдвигов,
  операции инкремента и декремента.
 Операция взятия остатка от деления(mod) является бинарной операцией и в языке СИ обозначается символом процента (%). Пример вычисления:
  int a = 10, b = 3, c;
  c = a % b; // с будет равно 1

Побитовые операции в СИ

 Побитовые операции языка СИ представлены тремя бинарными и одной унарной операцией. К бинарным побитовым операциям относятся:
  операция «И» (&),
  операция «ИЛИ» (|)
  операция «Исключающее ИЛИ» (^).

Вот таблица истинности для этих операций:

первый операнд второй операнд операция
и или исключающее или
0 0 0 0 0
1 0 0 1 1
0 1 0 1 1
1 1 1 1 0

Побитовые операции

Унарной побитовой операцией является операция отрицания, обозначаемая символом тильды (~). Пример:
  unsigned char a = 10, b; //a: 00001010 = 10
  b = ~a; //b: 11110101 = 245

Операции сдвига

Операции сдвига осуществляют побитовый сдвиг целого значения, указанного в первом операнде, вправо (символ >>) или влево (символ <<) на указанное во втором операнде целое число бит. Пример:
 unsigned char a = 10, b, c; //a: 00001010 = 10
 b = a << 2; //b: 00101000 = 40
 c = a >> 1; //c: 00000101 = 5

Операции инкремента и декремента

Операции инкремента (знак ++) и декремента (знак --) являются унарными и осуществляют увеличение и уменьшение целого значения на единицу соответственно.
 int a = 10, b, c;
 b = ++a //пред- инкремент b == 11
 c = a++; //пост- инкремент с == 11

 В современных языках программирования (в том числе и языке СИ стандарта С99) данные операции могут использоваться и для вещественных значений. Пример:
 double x = 12.5;
 x++;
 printf("%lf\n”,x); //вывод: 13.5

Операции отношения (сравнения)

В языках программирования операции отношения (сравнения) являются бинарными операциями, осуществляющими сравнение двух операндов и возвращающие результат сравнения в виде логического значения. В языке СИ принято логические значения ИСТИНА и ЛОЖЬ интерпретировать посредством целочисленных значений:
 0 – ЛОЖЬ, 1 – ИСТИНА.
Обозначение Название
> Больше
< Меньше
>= Больше или равно
<= Меньше или равно
== Равно
!= Не равно

Примеры
Несколько примеров использования операций сравнения:
 int a=5, b=4, c=10, x, y;
 x = a > b; //x == 1
 y = c == a; //y == 0

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

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

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


 Примеры логических операций:
  int a=1, b=0, c, d; //a – ИСТИНА, b – ЛОЖЬ
  c = a || b; //c == 1
  d = !b && a; //d == 1

Приоритеты операций

++, -- Операции пост- инкремента и декремента
() Вызов функции, группировка операций
Обращение к элементу массива
-> Обращение к полю структуры или объединения через указатель
. Обращение к полю структуры или объединения
++, -- Операции пред-инкремента и декремента
! Логическое «НЕ»
~ Бинарное отрицание(инверсия)
+, - Унарные плюс и минус
& Операция взятия адреса
* Разыменование указателя
sizeof Оператор определения размера
(type) Оператор преобразования типа
* Умножение
/ Деление
% Взятие остатка от деления
+ Сложение
- Вычитание
<<, >> Побитовые сдвиги влево и вправо
<, <=, >, >= Операции сравнения
==, != Операции сравнения
& Побитовое «И»
^ Побитовое «Исключающее ИЛИ»
| Побитовое «ИЛИ»
Логическое «И»
|| Логическое «ИЛИ»
?: Условная операция
= Оператор простого присвоения
*=, /=, %=, +=, -=, <<=, >>=, &=, ^=,|= Усовершенствованные операторы присвоения
, Запятая

Особенности трансляторов


 Не определяется порядок, в котором вычисляются аргументы функции при ее вызове. Поэтому следующий оператор может дать различные результаты при трансляции разными компиляторами:
  printf("%d %lf\n”, ++n, pow(2.0,n));
 Результат будет зависеть от того, получает ли n приращение до или после вызова функции pow. Чтобы решить проблему достаточно записать так:   n++;
  printf("%d %lf\n”, n,pow(2.0,n));

Схема автоматического приведения типа


 1.Если какой-либо из операторов имеет тип long double , то и другой приводится к long double .
 2.Иначе, если какой-либо из операторов имеет тип double , то и другой приводится к double .
 3.Иначе, если какой-либо из операторов имеет тип float , то и другой приводится к float .
 4.Иначе, для обоих операндов выполняется расширение целого типа; затем, если один из операндов имеет тип unsigned long int , то другой преобразуется в unsigned long int .
 5.Иначе, если один из операндов имеет тип long int , а другой – unsigned int , то результат зависит от того, представляет ли long int все значения unsigned int ; если это так, то операнд типа unsigned int
 6.приводится к типу long int ; если нет, то оба операнда преобразуются в unsigned long int .
 7.Иначе, если один из операндов имеет тип long int , то и другой приводится к long int .
 8.Иначе, оба операнда имеют тип int .

Оператор приведения типа

  int a = 15, b = 2; double r = 0.0;
  r = a / b; //r == 7.0

 Оператор приведения типа: (тип)выражение.
  r = (double)a / b; //Правильно
  r = (double) (a / b); //Неправильно

Условная операция


 В языке СИ присутствует так называемая условная операция, которая имеет следующий синтаксис:
  условие? выражение №1: выражение №2;
 Пример условной операции. Необходимо ввести с клавиатуры два вещественных значения и вывести на экран максимальное из этих значений:
  #include


  {
   double x,y;
   scanf("%lf %lf”,&x,&y);
   double max = (x > y) ? x: y;
   return 0;
  }

 Необходимо ввести с клавиатуры три вещественных значения и вывести на экран максимальное из этих значений:
  #include

Int main(int argc, char *argv)
  {
   double x, y, z;
   printf("Введите значения: ");
   scanf("%lf %lf %lf",&x,&y,&z);
   double max = (x > y) ? ((x > z) ? x: z): ((y > z) ? y:z);
   printf("Максимальное значение: %lf\n",max);
   return 0;
  }

 Вещественное число вводится с клавиатуры. Возвести число в четвертую степень, используя только две операции умножения.
  #include

Int main(int argc, char *argv)
  {
   double a;
   printf("Введите значение: ");
   scanf("%lf",&a);
   a *= (a *=a);
   printf("Результат: %lf\n",a);
   return 0;
  }

 Квадратное уравнение вида задается коэффициентами A, B и C. Определить какое количество корней имеет данное уравнение.
  #include

Int main(int argc, char *argv)
  {
   double a,b,c;
   printf("Введите коэффициенты A, B и С: ");
   scanf("%lf %lf %lf",&a,&b,&c);
   double d = b*b-4*a*c;
   int n = (d < 0.0)?0:(d > 0.0)?2:1;
   printf("Количество корней: %d\n",n);
   return 0;
  }

Операторы отношения и логические операторы

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

Ниже перечислены операторы отношения:

К числу логических относятся операторы, приведенные ниже:

Результатом выполнения оператора отношения или логического оператора является логическое значение типа bool .

В целом, объекты можно сравнивать на равенство или неравенство, используя операторы отношения == и!=. А операторы сравнения, = могут применяться только к тем типам данных, которые поддерживают отношение порядка. Следовательно, операторы отношения можно применять ко всем числовым типам данных. Но значения типа bool могут сравниваться только на равенство или неравенство, поскольку истинные (true) и ложные (false) значения не упорядочиваются. Например, сравнение true > false в C# не имеет смысла.

Рассмотрим пример программы, демонстрирующий применение операторов отношения и логических операторов:

Using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string args) { short d = 10, f = 12; bool var1 = true, var2 = false; if (d f) Console.WriteLine("d > f"); // Сравниванием переменные var1 и var2 if (var1 & var2) Console.WriteLine("Данный текст не выведется"); if (!(var1 & var2)) Console.WriteLine("!(var1 & var2) = true"); if (var1 | var2) Console.WriteLine("var1 | var2 = true"); if (var1 ^ var2) Console.WriteLine("var1 ^ var2 = true"); Console.ReadLine(); } } }

Логические операторы в C# выполняют наиболее распространенные логические операции. Тем не менее существует ряд операций, выполняемых по правилам формальной логики. Эти логические операции могут быть построены с помощью логических операторов, поддерживаемых в C#. Следовательно, в C# предусмотрен такой набор логических операторов, которого достаточно для построения практически любой логической операции, в том числе импликации. Импликация - это двоичная операция, результатом которой является ложное значение только в том случае, если левый ее операнд имеет истинное значение, а правый - ложное. (Операция импликации отражает следующий принцип: истина не может подразумевать ложь.)

Операция импликации может быть построена на основе комбинации логических операторов! и |:

Укороченные логические операторы

В C# предусмотрены также специальные, укороченные , варианты логических операторов И и ИЛИ, предназначенные для получения более эффективного кода. Поясним это на следующих примерах логических операций. Если первый операнд логической операции И имеет ложное значение (false), то ее результат будет иметь ложное значение независимо от значения второго операнда. Если же первый операнд логической операции ИЛИ имеет истинное значение (true), то ее результат будет иметь истинное значение независимо от значения второго операнда. Благодаря тому что значение второго операнда в этих операциях вычислять не нужно, экономится время и повышается эффективность кода .

Укороченная логическая операция И выполняется с помощью оператора && , а укороченная логическая операция ИЛИ - с помощью оператора || . Этим укороченным логическим операторам соответствуют обычные логические операторы & и |. Единственное отличие укороченного логического оператора от обычного заключается в том, что второй его операнд вычисляется только по мере необходимости.

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

В С++ существует три логические операции:

  1. Логическая операция И && , нам уже известная;
  2. Логическая операция ИЛИ || ;
  3. Логическая операция НЕ ! или логическое отрицание.

Логические операции образуют сложное (составное) условие из нескольких простых (два или более) условий. Эти операции упрощают структуру программного кода в несколько раз. Да, можно обойтись и без них, но тогда количество ифов увеличивается в несколько раз, в зависимости от условия. В следующей таблице кратко охарактеризованы все логические операции в языке программирования С++, для построения логических условий.

Сейчас следует понять разницу между логической операцией И и логической операцией ИЛИ , чтобы в дальнейшем не путаться. Пришло время познакомиться с типом данных bool –логический . Данный тип данных может принимать два значения: true (истина) и false (ложь). Проверяемое условие в операторах выбора имеет тип данных bool . Рассмотрим принцип работы следующей программы, и все будет понятно со всеми этими логическими операциями.

// or_and_not.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include using namespace std; int main(int argc, char* argv) { bool a1 = true, a2 = false; // объявление логических переменных bool a3 = true, a4 = false; cout << "Tablica istinnosti log operacii &&" << endl; cout << "true && false: " << (a1 && a2) << endl // логическое И << "false && true: " << (a2 && a1) << endl << "true && true: " << (a1 && a3) << endl << "false && false: " << (a2 && a4) << endl; cout << "Tablica istinnosti log operacii ||" << endl; cout << "true || false: " << (a1 || a2) << endl // логическое ИЛИ << "false || true: " << (a2 || a1) << endl << "true || true: " << (a1 || a3) << endl << "false || false: " << (a2 || a4) << endl; cout << "Tablica istinnosti log operacii !" << endl; cout << "!true: " << (! a1) << endl // логическое НЕ << "!false: "<< (! a2) << endl; system("pause"); return 0; }

Строки 9 и 10 вам должны быть понятны, так как здесь инициализируются переменные типа bool . Причем каждой переменной присваивается значение true или false . Начиная с 9-й строки и заканчивая 20-й , показано использование логических операций. Результат работы программы (см. Рисунок 1).

Tablica istinnosti log operacii && true && false: 0 false && true: 0 true && true: 1 false && false: 0 Tablica istinnosti log operacii || true || false: 1 false || true: 1 true || true: 1 false || false: 0 Tablica istinnosti log operacii ! !true: 0 !false: 1 Для продолжения нажмите любую клавишу. . .

Рисунок 1 — Логические операции С++

Наверное, у вас возникает вопрос, «А что это за нолики и единички?». Если есть вопрос, то на него нужно ответить. Отвечаю: «Нолик-это представление логического значения false (ложь), ну а единички – это логическое true (истина)». Коротко поясню некоторые моменты. Составное условие с использованием логического И истинно только в том случае, когда истинны оба простых условия. Во всех остальных случаях составное условие ложно. Составное условие с использованием логического ИЛИ ложно только в том случае, когда ложные оба простых условия. Во всех остальных случаях составное условие истинно. Логическое отрицание НЕ является унарной операцией, и она не комбинирует два условия, в отличие от логических операций И и ИЛИ , которые являются бинарными операциями. Логическое отрицание позволяет перевернуть смысл условия, что в некоторых случаях очень удобно. Условие с логическим отрицанием истинно в том случае, если это же условие ложно без отрицания, и наоборот.

Примечание. Все операции в результате дают значение типа bool

Операции сравнения и логические операции в результате дают значение типа bool, то есть true или false. Если же такое выражение встречается в контексте, требующем целого значения, true преобразуется в 1, а false – в 0. Вот фрагмент кода, подсчитывающего количество элементов вектора, меньших некоторого заданного значения:

Vector::iterator iter = ivec.beg-in() ; while (iter != ivec.end()) { // эквивалентно: e1em_cnt = e1em_cnt + (*iter < some_va1ue) // значение true/false выражения *iter < some_va1ue // превращается в 1 или 0 e1em_cnt += *iter < some_va1ue; ++iter; }

Мы просто прибавляем результат операции “меньше” к счетчику. (Пара += обозначает составной оператор присваивания, который складывает операнд, стоящий слева, и операнд, стоящий справа. То же самое можно записать более компактно: elem_count = elem_count + n. Мы рассмотрим такие операторы в разделе 4.4.)
Логическое И (&&) возвращает истину только тогда, когда истинны оба операнда. Логическое ИЛИ (||) дает истину, если истинен хотя бы один из операндов. Гарантируется, что операнды вычисляются слева направо и вычисление заканчивается, как только результирующее значение становится известно. Что это значит? Пусть даны два выражения:

Expr1 && expr2 expr1 || expr2

Если в первом из них expr1 равно false, значение всего выражения тоже будет равным false вне зависимости от значения expr2, которое даже не будет вычисляться. Во втором выражении expr2 не оценивается, если expr1 равно true, поскольку значение всего выражения равно true вне зависимости от expr2.
Подобный способ вычисления дает возможность удобной проверки нескольких выражений в одном операторе AND:

While (ptr != О && ptr->va1ue < upperBound && ptr->va1ue >= 0 && notFound(ia[ ptr->va1ue ])) { ... }

Указатель с нулевым значением не указывает ни на какой объект, поэтому применение к нулевому указателю операции доступа к члену вызвало бы ошибку (ptr->value). Однако, если ptr равен 0, проверка на первом шаге прекращает дальнейшее вычисление подвыражений. Аналогично на втором и третьем шагах проверяется попадание величины ptr->value в нужный диапазон, и операция взятия индекса не применяется к массиву ia, если этот индекс неправилен.
Операция логического НЕ дает true, если ее единственный оператор равен false, и наоборот. Например:

Bool found = false; // пока элемент не найден // и ptr указывает на объект (не 0) while (! found && ptr) { found = 1ookup(*ptr); ++ptr; }

Подвыражение

Дает true, если переменная found равна false. Это более компактная запись для

Found == false

Аналогично

Эквивалентно более длинной записи

If (found == true)

Использование операций сравнения достаточно очевидно. Нужно только иметь в виду, что, в отличие от И и ИЛИ, порядок вычисления операндов таких выражений не определен. Вот пример, где возможна подобная ошибка:

// Внимание! Порядок вычислений не определен! if (ia[ index++ ] < ia[ index ]) // поменять местами элементы

Программист предполагал, что левый операнд оценивается первым и сравниваться будут элементы ia и ia. Однако компилятор не гарантирует вычислений слева направо, и в таком случае элемент ia может быть сравнен сам с собой. Гораздо лучше написать более понятный и машинно-независимый код:

If (ia[ index ] < ia[ index+1 ]) // поменять местами элементы ++index;

Еще один пример возможной ошибки. Мы хотели убедиться, что все три величины ival, jval и kval различаются. Где мы промахнулись?

// Внимание! это не сравнение 3 переменных друг с другом if (ival != jva1 != kva1) // do something ...

Значения 0, 1 и 0 дают в результате вычисления такого выражения true. Почему? Сначала проверяется ival != jval, а потом итог этой проверки (true/false – преобразованной к 1/0) сравнивается с kval. Мы должны были явно написать:
if (ival != jva1 && ival != kva1 && jva1 != kva1)
// сделать что-то...

Упражнение 4.4

Найдите неправильные или непереносимые выражения, поясните. Как их можно изменить? (Заметим, что типы объектов не играют роли в данных примерах.)
(a) ptr->iva1 != 0
(с) ptr != 0 && *ptr++
(e) vec[ iva1++ ] <= vec[ ival ];
(b) ival != jva1 < kva1 (d) iva1++ && ival

Упражнение 4.5

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