На цьому уроці ми розглянемо логічні оператори І, АБО та НЕ в мові С++.
Логічні оператори
У той час як оператори порівняння використовуються для перевірки конкретної умови: помилкова вона чи істинна, вони можуть перевірити тільки одну умову за певний проміжок часу. Але бувають ситуації, коли потрібно протестувати відразу декілька умов. Наприклад, щоб дізнатися, чи виграли ми в лотерею, нам потрібно порівняти всі цифри купленого квитка з виграшними. Якщо в лотереї 6 цифр, то потрібно виконати 6 порівнянь, всі з яких мають бути true
.
Також іноді нам потрібно знати, чи є хоч одне з декількох умов істинним. Наприклад, ми не підемо сьогодні на роботу, якщо хворі або дуже втомилися, або якщо виграли в лотерею 🙂 Нам потрібно перевірити, чи є хоч одна з цих 3-х умов істинною. Як це зробити? За допомогою логічних операторів! Вони дозволяють перевірити відразу декілька умов за один раз.
У мові C++ є 3 логічних оператори:
Оператор | Символ | Приклад | Операція |
Логічне НЕ | ! | !x | true, якщо x — false і false, якщо x — true |
Логічне І | && | x && y | true, якщо x і y — true, в протилежному випадку — false |
Логічне АБО | || | x || y | true, якщо x чи y — true, в протилежному випадку — false |
Логічний оператор НЕ
Ми вже з ним стикалися на уроці про логічний тип даних bool.
Логічний оператор НЕ (!) | |
Операнд | Результат |
true | false |
false | true |
Якщо операндом є true
, то після застосування логічного НЕ результатом стане false
. Якщо ж операнд до застосування оператора НЕ є false
, то після його застосування стане true
. Іншими словами, логічний оператор НЕ змінює результат з true
на false
і навпаки. Він часто використовується в умовних виразах:
1 2 3 4 5 |
bool bTooLarge = (x > 100); // змінна bTooLarge буде true, якщо x > 100 if (!bTooLarge) // Робимо що-небудь з x else // Виводимо помилку |
Слід пам’ятати, що логічний оператор НЕ має дуже високий рівень пріоритету. Початківці часто роблять наступну помилку:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> int main() { int x = 5; int y = 7; if (!x == y) std::cout << "x does not equal y"; else std::cout << "x equals y"; return 0; } |
Результат виконання програми:
х equals у
Але х
не дорівнює у
, як це можливо? Оскільки пріоритет логічного оператора НЕ вище, ніж пріоритет оператора рівності, то вираз !х==у
обчислюється як (!х)==у
. Так як х
— це 5
, то !x
— це 0
. Умова 0==у
помилкова, тому виконується частина else!
Нагадування: Будь-яке ненульове ціле значення в логічному контексті є true
. Оскільки х = 5
, то х
обчислюється як true
, а ось !x = false
, тобто 0
. Використання цілих чисел в логічних операціях подібним чином може заплутати не тільки користувача, але і самого розробника, тому так робити не рекомендується!
Правильний спосіб написання вищенаведеної програми:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> int main() { int x = 5; int y = 7; if (!(x == y)) std::cout << "x does not equal y"; else std::cout << "x equals y"; return 0; } |
Спочатку обчислюється х==у
, а потім вже оператор НЕ змінює результат на протилежний.
Правило: Якщо логічний оператор НЕ повинен працювати з результатами роботи інших операторів, то інші оператори і їх операнди повинні знаходитися в круглих дужках.
Логічний оператор АБО
Якщо хоч одне з двох умов є істинним, то логічний оператор АБО є true
.
Логічний оператор АБО (||) | ||
Лівий операнд | Правий операнд | Результат |
false | false | false |
false | true | true |
true | false | true |
true | true | true |
Розглянемо наступну програму:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> int main() { std::cout << "Enter a number: "; int value; std::cin >> value; if (value== 0 || value== 1) std::cout << "You picked 0 or 1" << std::endl; else std::cout << "You did not pick 0 or 1" << std::endl; return 0; } |
Тут ми використали логічний оператор АБО, щоб перевірити, чи є хоч одна з двох умов істинною: ліва (value==0
) або права (value==1
). Якщо хоч одна з умов є true
або обидві відразу є true
, то виконуватися буде стейтмент if. Якщо жодна з умов не є true
, то результат — false
і виконуватися буде стейтмент else.
Ви можете поєднувати відразу декілька умов:
1 2 |
if (value == 0 || value == 1 || value == 2 || value == 3) std::cout << "You picked 0, 1, 2, or 3" << std::endl; |
Початківці іноді плутають логічне АБО (||
) з побітовим АБО (|
). Хоча у них і однакові назви, функціонал у них різний.
Логічний оператор І
Тільки за умови, що обидва операнди будуть істинними, логічний оператор І буде true
. Якщо ні, то тоді false
.
Логічний оператор І (&&) | ||
Лівий операнд | Правий операнд | Результат |
false | false | false |
false | true | false |
true | false | false |
true | true | true |
Наприклад, ми хочемо знати, чи знаходиться значення змінної х
в діапазоні від 10 до 20. Тут у нас є дві умови: ми повинні перевірити, чи є х
більше 10 і чи є х
менше 20.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> int main() { std::cout << "Enter a number: "; int value; std::cin >> value ; if (value > 10 && value < 20) std::cout << "Your value is between 10 and 20" << std::endl; else std::cout << "Your value is not between 10 and 20" << std::endl; return 0; } |
Якщо обидві умови істинні, то виконується частина if. Якщо ж хоча б одна або відразу обидві умови помилкові, то виконується частина else.
Як і з логічним АБО, ми можемо комбінувати відразу декілька умов І:
1 2 3 4 |
if (value > 10 && value < 20 && value != 16) // Робимо що-небудь else // Робимо що-небудь інше |
Короткий цикл обчислення
Для того, щоб логічне І повертало true
, обидва операнди повинні бути істинними. Якщо перший операнд обчислюється як false
, то оператор І повинен відразу повертати false
незалежно від результату другого операнду (навіть без його обробки). Це називається коротким циклом обчислення і виконується він, в першу чергу, в цілях оптимізації.
Аналогічно, якщо перший операнд логічного АБО є 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 не застосовується короткий цикл обчислення. Однак його легко можна зімітувати, використовуючи оператор нерівності (!=
):
1 |
if (a != b) ... // a XOR b (припускається, що a і b - типу bool) |
Можна також збільшити кількість операндів:
1 |
if (a != b != c != d) ... // a XOR b XOR c XOR d (припускається, що a, b, c і d - типу bool) |
Слід зазначити, що вищенаведені шаблони XOR працюють тільки, якщо операнди є логічного типу даних (а не цілочисельних типів). Якщо ви хочете, щоб це працювало і з цілими числами, то використовуйте оператор static_cast.
Форма XOR, яка працює і з іншими типами даних (за допомогою оператора static_cast ми можемо конвертувати будь-який тип даних в bool):
1 |
if (static_cast<bool>(a) != static_cast<bool>(b) != static_cast<bool>(c) != static_cast<bool>(d)) ... // a XOR b XOR c XOR d, для будь-якого типу, який може бути конвертований в 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