Урок №43. Логические операторы: И, ИЛИ, НЕ

  Юрий  | 

  |

  Обновл. 11 Сен 2021  | 

 195908

 ǀ   19 

На этом уроке мы рассмотрим логические операторы И, ИЛИ и НЕ в языке С++.

Логические операторы

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

Также иногда нам нужно знать, является ли хоть одно из нескольких условий истинным. Например, мы не пойдем сегодня на работу, если больны или слишком устали, или если выиграли в лотерею. 🙂 Нам нужно проверить, является ли хоть одно из этих трех условий истинным. Как это сделать? С помощью логических операторов! Они позволяют проверить сразу несколько условий за раз.

В языке C++ есть 3 логических оператора:

Оператор Символ Пример Операция
Логическое НЕ ! !x true, если x — false и false, если x — true
Логическое И && x && y true, если x и y — true, в противном случае — false
Логическое ИЛИ || x || y true, если x или y — true, в противном случае — false

Логический оператор НЕ


Мы уже с ним сталкивались на уроке №34.

Логический оператор НЕ (!)
Операнд Результат
true false
false true

Если операндом является true, то, после применения логического НЕ, результатом будет false. Если же операнд до применения оператора НЕ был false, то после его применения станет true. Другими словами, логический оператор НЕ меняет результат на противоположный начальному значению. Он часто используется в условных выражениях:

Следует помнить, что логический оператор НЕ имеет очень высокий уровень приоритета. Новички часто совершают следующую ошибку:

Результат выполнения программы:

х equals у

Но х ведь не равно у, как это возможно? Поскольку приоритет логического оператора НЕ выше, чем приоритет оператора равенства, то выражение ! х == у обрабатывается как (! х) == у. Так как х — это 5, то !x — это 0. Условие 0 == у ложное, поэтому выполняется часть else!

Напоминание: Любое ненулевое целое значение в логическом контексте является true. Так как х = 5, то х вычисляется как true, а вот !x = false, т.е. 0. Использование целых чисел в логических операциях подобным образом может запутать не только пользователя, но и самого разработчика, поэтому так не рекомендуется делать!

Правильный способ написания программы, приведенной выше:

Сначала обрабатывается х == у, а затем уже оператор НЕ изменяет результат на противоположный.

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

Логический оператор ИЛИ

Если хоть одно из двух условий является истинным, то логический оператор ИЛИ является true.

Логический оператор ИЛИ (||)
Левый операнд Правый операнд Результат
false false false
false true true
true false true
true true true

Рассмотрим следующую программу:

Здесь мы использовали логический оператор ИЛИ, чтобы проверить, является ли хоть одно из двух условий истинным: левое (value == 0) или правое (value == 1). Если хоть одно из условий — true или оба сразу true, то выполняться будет стейтмент if. Если ни одно из условий не является true, то результат — false и выполняться будет стейтмент else.

Вы можете связать сразу несколько условий:

Новички иногда путают логическое ИЛИ (||) с побитовым ИЛИ (|). Хоть у них и одинаковые названия, функции они выполняют разные.

Логический оператор И


Только при условии, что оба операнда будут истинными, логический оператор И будет true. Если нет, тогда — false.

Логический оператор И (&&)
Левый операнд Правый операнд Результат
false false false
false true false
true false false
true true true

Например, мы хотим узнать, находится ли значение переменной х в диапазоне от 10 до 20. Здесь у нас есть два условия: мы должны проверить, является ли х больше 10 и является ли х меньше 20.

Если оба условия истинны, то выполняется часть if. Если же хоть одно или сразу оба условия ложные, то выполняется часть else.

Как и с логическим ИЛИ, мы можем комбинировать сразу несколько условий И:

Короткий цикл вычислений

Для того, чтобы логическое И возвращало true, оба операнда должны быть истинными. Если первый операнд вычисляется как false, то оператор И должен сразу возвращать false независимо от результата второго операнда (даже без его обработки). Это называется коротким циклом вычисления (англ. «short circuit evaluation») и выполняется он, в первую очередь, в целях оптимизации.

Аналогично, если первый операнд логического ИЛИ является true, то и всё условие будет true (даже без обработки второго операнда).

Как и в случае с оператором ИЛИ, новички иногда путают логическое И (&&) с побитовым И (&).

Использование логических операторов И/ИЛИ


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

Многие программисты думают, что логические И и ИЛИ имеют одинаковый приоритет (или забывают, что это не так), так же как и сложение/вычитание или умножение/деление. Тем не менее, приоритет логического И выше приоритета ИЛИ. Таким образом, операции с оператором И всегда будут вычисляться первыми (если только операции с ИЛИ не находятся в круглых скобках).

Рассмотрим следующее выражение: value1 || value2 && value3. Поскольку приоритет логического И выше, то обрабатываться выражение будет так:

value1 || (value2 && value3)

А не так:

(value1 || value2) && value3

Хорошей практикой является использование круглых скобок с операциями. Это предотвратит ошибки приоритета, увеличит читабельность кода и чётко даст понять компилятору, как следует обрабатывать выражения. Например, вместо того, чтобы писать value1 && value2 || value3 && value4, лучше записать (value1 && value2) || (value3 && value4).

Законы Де Моргана

Многие программисты совершают ошибку, думая, что !(x && y) — это то же самое, что и !x && !y. К сожалению, вы не можете использовать логическое НЕ подобным образом.

Законы Де Моргана гласят, что !(x && y) эквивалентно !x || !y, а !(x || y) эквивалентно !x && !y.

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

А где же побитовое исключающее ИЛИ (XOR)?

Побитовое исключающее ИЛИ (XOR) — это логический оператор, который используется в некоторых языках программирования для проверки на истинность нечётного количества условий.

Побитовое исключающее ИЛИ (XOR)
Левый операнд Правый операнд Результат
false false false
false true true
true false true
true true false

В языке C++ нет такого оператора. В отличии от логических И/ИЛИ, к XOR не применяется короткий цикл вычислений. Однако его легко можно сымитировать, используя оператор неравенства (!=):

Можно также расширить количество операндов:

Следует отметить, что вышеприведенные шаблоны XOR работают только, если операнды имеют логический (а не целочисленный) тип данных. Если вы хотите, чтобы это работало и с целыми числами, то используйте оператор static_cast.

Форма XOR, которая работает и с другими типами данных (с помощью оператора static_cast мы можем конвертировать любой тип данных в тип bool):

Тест

Какой результат следующих выражений?

   Выражение №1: (true && true) || false

   Выражение №2: (false && true) || true

   Выражение №3: (false && true) || false || true

   Выражение №4: (5 > 6 || 4 > 3) && (7 > 8)

   Выражение №5: !(7 > 6 || 3 > 4)

Ответ

Примечание: В ответах объяснение выполняется с помощью стрелочки (=>). Например, (true || false) => true означает, что результатом выражения (true || false) является true.

   Выражение №1: (true && true) || false => true || false => true

   Выражение №2: (false && true) || true => false || true => true

   Выражение №3: (false && true) || false || true => false || false || true => false || true => true

   Выражение №4: (5 > 6 || 4 > 3) && (7 > 8) => (false || true) && false => true && false => false

   Выражение №5: !(7 > 6 || 3 > 4) => !(true || false) => !true => false

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (564 оценок, среднее: 4,94 из 5)
Загрузка...

Комментариев: 19

  1. Вася:

    Вместо static_cast<bool> можно использовать двойное отрицание !!, так тоже работает и код значительно короче

  2. Лера:

    А какую нужно использовать библиотеку что бы использовать логические операции?

  3. Александр:

    Не понял про преобразование в bool. Если XOR работает только с bool, то почему данный код работает?

    1. ОЛЕГ:

      Как написано в уроке 34: "На самом деле, логические значения не сохраняются как true или false. Они обрабатываются в виде целых чисел: вместо true — единица, вместо false — ноль". Поэтому Ваша программа работает.

  4. Artur:

    спасибо огромное!
    очень понятно все изложено!

  5. Nick:

    Подскажите пожалуйста, в соответствии со стандартом C++, оператор && всегда вычисляет оба операнда или только если первый из них возвращает true?

    В выражении:

    будут ли всегда выполнены обе функции f1() и f2()?

    1. Nick:

      Нашёл ответ:

      если f1() или f2() возвращает false, то будет выполнена только одна из функций, но какая именно стандартом не регламентируется

    2. Сергей:

      в данном выражении обе функции выполняться только в случае если первая будет true

      1. Nick:

        Нет, порядок выполнения операндов в операторах в C и C++ не регламентирован. Может вначале выполниться вторая функция и если она вернёт false, первая не выполнится. Но может и в обратном порядке. Отсюда можно словить интересные побочные эффекты (side effects), поэтому в этих языках программирования вызываемые в операторах функции не должны иметь влияния друг на друга.

        Другое дело, например bash или Perl и другие языки где порядок выполнения операндов в операторах регламентирован (в основном слева направо). Там действительно всегда сначала вычисляеься левый операнд, а потом, если необходимо, правый

        1. Даниил:

          Чувак, не вводи в заблуждение:
          §5.14/1:
          The && operator groups left-to-right. The operands are both contextually converted to type bool (Clause 4). The result is true if both operands are true and false otherwise. Unlike &, && guarantees left-to-right evaluation: the second operand is not evaluated if the first operand is false.

        2. Bob:

          Смотри урок 38, там приоритет в табличке. && обрабатывается с лева на право)))

  6. AleksTs:

    Не перестаю восхищаться изложенному материалу! Огромное спасибо за проделанную работу!

    1. Фото аватара Юрий:

      Пожалуйста)) Мне приятно)

  7. Александр:

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

    лучше написать:

  8. Аркадий:

    "Для реализации оператора XOR" используют ^. В с++ — это именно XOR, а не возведение в степень.

    1. Tosha:

      ^ — это битовый оператор, а данная статья — о логических. Логического XOR в плюсах нет.
      Для побитового исключения используется именно ^. Для реализации же логического XOR использовать ^ крайне глупо потому что:
      1. Сравнивая булевы значения будет очевидней(и эффективней, хоть эти наносекунды уже никого не интересуют) написать true != false, чем true ^ false.
      2. В случае сравнения не булевых значений стоит продолжать существующую логику. На примере чисел: любое число, отличное от нуля — это true. Следовательно нельзя просто написать 1 ^ 2 — потому что эта операция вернет 3, что интерпретируется, как true. Почему? Потому что любое не нулевое число должно восприниматься, как истина, следовательно 1 -> true, 2 -> true. результатом XOR между ними должен быть false. Таким образом доказываем неприминимость ^, приходим к необходимости преобразования операндов в булевы значения и смотрим пункт 1.

    2. Александр:

      ^ — это побитовый оператор… ожидать от него поведения логического XOR — нарываться на потенциальные алгоритмические ошибки…
      можно в качестве XOR использовать оператор != для логических выражений (тоже некорректно работает, если Вы ожидаете неявных преобразований типов)

  9. Светлана:

    Скажите, пожалуйста, почему в примере из урока нужно использовать if (!(x == y)), а не if (x != y) ?

    1. Фото аватара Юрий:

      Можно использовать и второй вариант, но != зачастую используется для реализации оператора XOR и в цепочке операндов, а !(==) для проверки на неравенство определенного выражения. Но использовать можно как первый, так и второй вариант — работать будет.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *