5.7 – Логические операторы
Хотя операторы отношения (сравнения) могут использоваться для проверки того, является ли конкретное условие истинным или ложным, они могут проверять только одно условие за раз. Но часто нам нужно знать, выполняются ли одновременно несколько условий. Например, чтобы проверить, выиграли ли мы в лотерею, мы должны сравнить, все ли выбранные нами числа совпадают с выигрышными числами. В лотерее с 6 числами это будет включать 6 сравнений, и все они должны быть верными. В других случаях нам нужно знать, выполняется ли какое-либо из нескольких условий. Например, мы можем решить пропустить работу сегодня, если мы заболели, или если мы слишком устали, или если мы выиграли в лотерею в нашем предыдущем примере. Это потребует проверки того, верно ли какое-либо из трех сравнений.
Возможность тестирования несколько условий нам дают логические операторы.
В C++ есть 3 логических оператора:
Оператор | Обозначение | Пример использования | Операция |
---|---|---|---|
Логическое НЕ (NOT) | ! | !x | true , если x равен false ; или false , если x равен true |
Логическое И (AND) | && | x && y | true , если и x ,и y равны true ; в противном случае – false |
Логическое ИЛИ (OR) | || | x || y | true , если x или y равен true ; в противном случае – false |
Логическое НЕ (NOT)
Вы уже сталкивались с унарным оператором логического НЕ в уроке «4.9 – Логические (булевы) значения». Мы можем резюмировать эффекты логического НЕ следующим образом:
Операнд | Результат |
---|---|
true | false |
false | true |
Если операнд логического НЕ вычисляется как true
, логическое НЕ вычисляется как false
. Если операнд логического НЕ вычисляется как false
, логическое НЕ вычисляется как true
. Другими словами, логическое НЕ меняет логическое значение с true
на false
и наоборот.
Логическое НЕ часто используется в условных выражениях:
bool tooLarge { x > 100 }; // tooLarge равно true, если x > 100
if (!tooLarge)
// делаем что-нибудь с x
else
// выводим ошибку
Следует остерегаться того, что логическое НЕ имеет очень высокий уровень приоритета. Начинающие программисты часто делают следующую ошибку:
#include <iostream>
int main()
{
int x{ 5 };
int y{ 7 };
if (!x > y)
std::cout << x << " is not greater than " << y << '\n';
else
std::cout << x << " is greater than " << y << '\n';
return 0;
}
Эта программа напечатает:
5 is greater than 7
Но x
не больше y
, так как же это возможно? Ответ заключается в том, что поскольку оператор логического НЕ имеет более высокий приоритет, чем оператор «больше чем», выражение !x > y
фактически вычисляется как (!x) > y
. Поскольку x
равно 5, !x
вычисляется как 0, а 0 > y
равно false
, поэтому выполняется инструкция else
!
Правильный способ написать приведенный выше фрагмент:
#include <iostream>
int main()
{
int x{ 5 };
int y{ 7 };
if (!(x > y))
std::cout << x << " is not greater than " << y << '\n';
else
std::cout << x << " is greater than " << y << '\n';
return 0;
}
Таким образом, сначала будет вычислено x > y
, а затем логическое НЕ инвертирует логический результат.
Лучшая практика
Если логическое НЕ предназначено для работы с результатом других операторов, другие операторы и их операнды должны быть заключены в круглые скобки.
Простое использование логического НЕ, например if (!value)
, не требует скобок, потому что приоритет здесь не играет роли.
Логическое ИЛИ (OR)
Оператор логического ИЛИ используется для проверки того, выполняется ли одно из двух условий. Если значение левого операнда истинно, или значение правого операнда истинно, или оба значения истинны, то логический оператор ИЛИ возвращает true
. В противном случае он вернет false
.
Левый операнд | Правый операнд | Результат |
---|---|---|
false | false | false |
false | true | true |
true | false | true |
true | true | true |
Например, рассмотрим следующую программу:
#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\n";
else
std::cout << "You did not pick 0 or 1\n";
return 0;
}
В этом случае мы используем логический оператор ИЛИ, чтобы проверить, истинно ли левое условие (value == 0
) или правое условие (value == 1
). Если одно из них (или оба) истинны, логический оператор ИЛИ принимает значение true
, что означает выполнение инструкции if
. Если ни одно из них не является истинным, результат логического оператора ИЛИ будет false
, что означает выполнение инструкции else
.
Вы можете связать вместе множество операторов логического ИЛИ:
if (value == 0 || value == 1 || value == 2 || value == 3)
std::cout << "You picked 0, 1, 2, or 3\n";
Начинающие программисты иногда путают оператор логическое ИЛИ (||
) с оператором побитовое ИЛИ (|
) (который будет рассмотрен позже). Несмотря на то, что у них обоих в названии есть «ИЛИ», они выполняют разные функции. Их смешивание, вероятно, приведет к неверным результатам.
Логическое И (AND)
Логический оператор И используется для проверки истинности обоих операндов. Если оба операнда равны true
, логическое И возвращает true
. В противном случае возвращается false
.
Левый операнд | Правый операнд | Результат |
---|---|---|
false | false | false |
false | true | false |
true | false | false |
true | true | true |
Например, нам может потребоваться узнать, находится ли значение переменной x
в диапазоне от 10 до 20. На самом деле это два условия: нам нужно знать, больше ли x
, чем 10, а также меньше ли x
, чем 20.
#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\n";
else
std::cout << "Your value is not between 10 and 20\n";
return 0;
}
В этом случае мы используем оператор логическое И, чтобы проверить, истинны ли левое условие (value > 10
) и правое условие (value < 20
). Если оба условия истинны, оператор логическое И принимает значение true
, и выполняется инструкция if
. Если ни одно из условий или хотя бы одно из них не соответствует истине, оператор логическое И принимает значение false
, и выполняется инструкция else
.
Как и в случае с логическим ИЛИ, вы можете связать вместе множество операторов логическое И:
if (value > 10 && value < 20 && value != 16)
// делаем что-то
else
// делаем что-то другое
Если все эти условия верны, будет выполнена инструкция if
. Если какое-либо из этих условий ложно, будет выполняться инструкция else
.
Вычисление по короткой схеме
Чтобы логическое И возвращало true
, оба операнда должны иметь значение true
. Если первый операнд вычисляется как false
, логическое И знает, что оно должно возвращать false
, независимо от того, вычисляется ли второй операнд как true
или false
. В этом случае оператор логическое И немедленно вернет false
, даже не вычисляя второй операнд! Это известно как вычисление по короткой схеме и выполняется в основном в целях оптимизации.
Точно так же, если первый операнд для логического ИЛИ равен true
, тогда всё условие ИЛИ должно вычисляться как true
, и второй операнд не вычисляется.
Вычисление по короткой схеме дает еще одну возможность показать, почему операторы, вызывающие побочные эффекты, не должны использоваться в составных выражениях. Рассмотрим следующий фрагмент:
if (x == 1 && ++y == 2)
// сделать что-то
Если x
не равно 1, всё условие должно быть ложным, поэтому ++y
никогда не вычисляется! Таким образом, y
будет инкрементироваться только в том случае, если x
равен 1, что, вероятно, не совсем то, что задумывал программист!
Предупреждение
Вычисление по короткой схеме может привести к тому, что логическое ИЛИ и логическое И не будут вычислять один операнд. Избегайте использования выражений с побочными эффектами в выражениях с этими операторами.
Как и в случае с логическим и побитовым ИЛИ, начинающие программисты иногда путают оператор логическое И (&&
) с оператором побитовое И (&
).
Смешивание И и ИЛИ
Смешивания операторов логическое И и логическое ИЛИ в одном выражении часто невозможно избежать, но это область, полная потенциальных опасностей.
Многие программисты предполагают, что логическое И и логическое ИЛИ имеют одинаковый приоритет (или забывают, что это не так), точно так же, как сложение/вычитание и умножение/деление. Однако логическое И имеет более высокий приоритет, чем логическое ИЛИ, поэтому операторы логическое И будут вычисляться перед операторами логическое ИЛИ (если они не заключены в скобки).
Начинающие программисты часто пишут такие выражения, как 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) – это логический оператор, представленный на некоторых языках, который используется для проверки истинности нечетного числа условий.
Левый операнд | Правый операнд | Результат |
---|---|---|
false | false | false |
false | true | true |
true | false | true |
true | true | false |
В C++ нет оператора логическое исключающее ИЛИ. В отличие от логического ИЛИ или логического И, логическое исключающее ИЛИ не может быть вычислено по короткой схеме. По этой причине создание оператора логическое исключающее ИЛИ из операторов логического ИЛИ и логического И является сложной задачей. Однако вы можете легко имитировать логическое исключающее ИЛИ (XOR), используя оператор неравенства (!=
):
if (a != b) ... // a XOR b, предполагая, что a и b - логические значения
Это выражение можно расширить до нескольких операндов следующим образом:
if (a != b != c != d) ... // a XOR b XOR c XOR d, предполагая, что a, b, c и d
// являются логическими значениями
Обратите внимание, что приведенные выше шаблоны логического исключающего ИЛИ работают только в том случае, если операнды являются логического типа (не целочисленными значениями). Если вам нужна форма логического исключающего ИЛИ, которая работает с не-логическими операндами, вы можете использовать static_cast
для преобразования их в bool
:
// a XOR b XOR c XOR d, для любого типа, который можно преобразовать в bool
if (static_cast<bool>(a) != static_cast<bool>(b) != static_cast<bool>(c) != static_cast<bool>(d)) ...
Небольшой тест
Вопрос 1
Вычислите следующие выражения.
Примечание: в ответах мы «объясняем нашу работу», показывая вам шаги, предпринятые для получения окончательного ответа. Шаги разделены символом →. Выражения, которые были проигнорированы из-за правила вычисления по короткой схеме, помещены в квадратные скобки. Например,
(1 < 2 || 3 != 3) →
(true || [3 != 3]) →
(true) →
true
означает, что мы вычислили (1 <2 || 3 != 3)
, чтобы прийти к (true || [3 != 3])
, и вычислили его, чтобы прийти к true
. 3 != 3 никогда не выполнялся из-за вычисления по короткой схеме.
a) (true && true) || false
Ответ
(true && true) || false →
true || [false] →
true
b) (false && true) || true
Ответ
(false && [true]) || true →
false || true →
true
Вычисление по короткой схеме имеет место быть, если первый операнд ||
равен true
.
c) (false && true) || false || true
Ответ
(false && [true]) || false || true →
false || false || true →
false || true →
true
d) (5 > 6 || 4 > 3) && (7 > 8)
Ответ
(5 > 6 || 4 > 3) && (7 > 8) →
(false || true) && false →
true && false →
false
e) !(7 > 6 || 3 > 4)
Ответ
!(7 > 6 || 3 > 4) →
!(true || [3 > 4]) →
!true →
false