Логические выражения и операторы. Урок 6 курса «Python. Введение в программирование»
Логические выражения и логический тип данных
Часто в реальной жизни мы соглашаемся с каким-либо утверждением или отрицаем его. Например, если вам скажут, что сумма чисел 3 и 5 больше 7, вы согласитесь, скажете: «Да, это правда». Если же кто-то будет утверждать, что сумма трех и пяти меньше семи, то вы расцените такое утверждение как ложное.
Подобные фразы предполагают только два возможных ответа – либо «да», когда выражение оценивается как правда/истина, либо «нет», когда утверждение оценивается как ошибочное/ложное. В программировании и математике если результатом вычисления выражения может быть лишь истина или ложь, то такое выражение называется логическим.
Например, выражение 4 > 5 является логическим, так как его результатом является либо правда, либо ложь. Выражение 4 + 5 не является логическим, так как результатом его выполнения является число.
На позапрошлом уроке мы познакомились с тремя типами данных – целыми и вещественными числами, а также строками. Сегодня введем четвертый – bool
). Его также называют булевым. У этого типа всего два возможных значения: True (правда) и False (ложь).
>>> a = True >>> type(a) <class 'bool'> >>> b = False >>> type(b) <class 'bool'>
Здесь переменной a было присвоено значение True
, после чего с помощью встроенной в Python функции type()
проверен ее тип. Интерпретатор сообщил, что это переменная класса bool
. Понятия «класс» и «тип данных» в данном случае одно и то же. Переменная b также связана с булевым значением.
В программировании False
обычно приравнивают к нулю, а True
– к единице. Чтобы в этом убедиться, можно преобразовать булево значение к целочисленному типу:
>>> int(True) 1 >>> int(False) 0
Возможно и обратное. Можно преобразовать какое-либо значение к булевому типу:
>>> bool(3.4) True >>> bool(-150) True >>> bool(0) False >>> bool(' ') True >>> bool('') False
И здесь работает правило: всё, что не 0 и не пустота, является правдой.
Логические операторы
Говоря на естественном языке (например, русском) мы обозначаем сравнения словами «равно», «больше», «меньше». В языках программирования используются специальные знаки, подобные тем, которые используются в математике: >
(больше), <
(меньше), >=
(больше или равно), <=
(меньше или равно), ==
(равно), !=
(не равно).
Не путайте операцию присваивания значения переменной, обозначаемую в языке Python одиночным знаком «равно», и операцию сравнения (два знака «равно»). Присваивание и сравнение – разные операции.
>>> a = 10 >>> b = 5 >>> a + b > 14 True >>> a < 14 - b False >>> a <= b + 5 True >>> a != b True >>> a == b False >>> c = a == b >>> a, b, c (10, 5, False)
В данном примере выражение c = a == b
состоит из двух подвыражений. Сначала происходит сравнение (==
) переменных a и b. После этого результат логической операции присваивается переменной c. Выражение a, b, c
просто выводит значения переменных на экран.
Сложные логические выражения
Логические выражения типа kbyte >= 1023
являются простыми, так как в них выполняется только одна логическая операция. Однако, на практике нередко возникает необходимость в более сложных выражениях. Может понадобиться получить ответа «Да» или «Нет» в зависимости от результата выполнения двух простых выражений. Например, «на улице идет снег или дождь», «переменная news больше 12 и меньше 20″.
В таких случаях используются специальные операторы, объединяющие два и более простых логических выражения. Широко используются два оператора – так называемые логические И (and) и ИЛИ (or).
Чтобы получить True
при использовании оператора and
, необходимо, чтобы результаты обоих простых выражений, которые связывает данный оператор, были истинными. Если хотя бы в одном случае результатом будет False
, то и все сложное выражение будет ложным.
Чтобы получить True
при использовании оператора or
, необходимо, чтобы результат хотя бы одного простого выражения, входящего в состав сложного, был истинным. В случае оператора
сложное выражение становится ложным лишь тогда, когда ложны оба составляющие его простые выражения.
Допустим, переменной x было присвоено значение 8 (x = 8
), переменной y присвоили 13 (y = 13
). Логическое выражение y < 15 and x > 8
будет выполняться следующим образом. Сначала выполнится выражение y < 15
. Его результатом будет True
. Затем выполнится выражение x > 8
. Его результатом будет False
. Далее выражение сведется к True and False
, что вернет False
.
>>> x = 8 >>> y = 13 >>> y < 15 and x > 8 False
Если бы мы записали выражение так: x > 8 and y < 15
, то оно также вернуло бы False
.
y < 15
не выполнялось бы интерпретатором, так как его незачем выполнять. Ведь первое простое логическое выражение (x > 8
) уже вернуло ложь, которая, в случае оператора and
, превращает все выражение в ложь.В случае с оператором or
второе простое выражение проверяется, если первое вернуло ложь, и не проверяется, если уже первое вернуло истину. Так как для истинности всего выражения достаточно единственного True
, неважно по какую сторону от or
оно стоит.
>>> y < 15 or x > 8 True
В языке Python есть еще унарный логический оператор not
, то есть отрицание. Он превращает правду в ложь, а ложь в правду. Унарный он потому, что применяется к одному выражению, стоящему после него, а не справа и слева от него как в случае бинарных
и or
.
>>> not y < 15 False
Здесь у < 15
возвращает True
. Отрицая это, мы получаем False
.
>>> a = 5 >>> b = 0 >>> not a False >>> not b True
Число 5 трактуется как истина, отрицание истины дает ложь. Ноль приравнивается к False
. Отрицание False
дает True
.
Практическая работа
Присвойте двум переменным любые числовые значения.
Используя переменные из п. 1, с помощью оператора
and
составьте два сложных логических выражения, одно из которых дает истину, другое – ложь.Аналогично выполните п. 2, но уже с оператором
or
.Попробуйте использовать в логических выражениях переменные строкового типа. Объясните результат.
Напишите программу, которая запрашивала бы у пользователя два числа и выводила бы
True
илиFalse
в зависимости от того, больше первое число второго или нет.
Примеры решения и дополнительные уроки в pdf-версии курса
5.7 – Логические операторы
Добавлено 8 мая 2021 в 18:37
Хотя операторы отношения (сравнения) могут использоваться для проверки того, является ли конкретное условие истинным или ложным, они могут проверять только одно условие за раз. Но часто нам нужно знать, выполняются ли одновременно несколько условий. Например, чтобы проверить, выиграли ли мы в лотерею, мы должны сравнить, все ли выбранные нами числа совпадают с выигрышными числами. В лотерее с 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
Оригинал статьи:
- 5.7 — Logical operators
Теги
C++ / CppLearnCppДля начинающихЛогические операторЛогические типы даныхЛогическое И (AND)Логическое ИЛИ (OR)Логическое исключающее ИЛИ (XOR)Логическое НЕ (NOT)ОбучениеОператор (программирование)ПрограммированиеНазад
Оглавление
Вперед
Общие сведения о логических операторах C++ | Udacity
Простые союзы, такие как «и» и «или», позволяют нам связать наши идеи — даже самые сложные.
Эти два мощных слова могут играть не меньшую роль в языке программирования C++, где они используются в качестве логических операторов.
Логические операторы необходимы для создания более сложного и динамичного потока управления программой. В этой статье рассказывается, как создавать логические операторы и когда их использовать.
Что такое операторы в C++?
Операторы — это символы, используемые в C++ для выполнения вычислений над переменными и значениями. Когда вы будете учиться, чтобы стать разработчиком C++, вы быстро увидите, что операторы играют важную роль в таких областях, как арифметические, реляционные и логические (истинные или ложные) утверждения в коде.
C++ использует логические значения для проверки истинности или ложности операторов отношения. Логические значения могут возвращать только 1 (истина) или 0 (ложь) в зависимости от результата сравнения. Как мы увидим ниже, мы можем использовать эти операторы несколькими способами, чтобы привести программу к определенному результату.
Во-первых, давайте рассмотрим, как мы можем использовать реляционные операторы для получения истинных или ложных выходных данных.
Реляционные операторы
Реляционные операторы, как кратко упоминалось выше, работают с переменными с определенными значениями и дают логический результат. Они используют такие символы, как ==, !=, <= и >, чтобы проверить, являются ли два операнда одинаковыми, разными, больше или меньше друг друга. Эти операторы выводят 1, если утверждение истинно, и 0, если ложно.
Логические операторы
Логические операторы работают только с логическими значениями (или выражениями, подобными реляционным операторам, которые возвращают логические значения) и дают собственный логический результат. В C++ для логических вычислений используются операторы !, && и ||.
Использование логических операторов в C++?
Как мы увидим, логические операторы хорошо подходят для проверки правильности двух (или более) сравнительных операций. Затем операторы выводят конкретный ответ, основанный на характере оператора и истинности одного или обоих операндов. В C++ мы часто видим это в форме оператора if/else.
Прежде чем мы сможем подробнее рассмотреть, где в коде часто встречаются логические операторы, нам сначала нужно понять синтаксис, стоящий за ними. Всего имеется три логических оператора:
Оператор «и» (&&)
Логический оператор «и» просматривает операнды на любой из своих сторон и возвращает «истина», только если оба утверждения истинны. Если хотя бы одно из двух утверждений ложно, логическое «и» вернет ложь.
Ниже приведен практический пример использования оператора && в C++:
#include |
Выше мы просим пользователя указать номер. Наш логический оператор «и» проверяет, является ли число больше 0, а также меньше или равно 10. Если оба эти утверждения верны, число должно быть между 1 и 10, и мы можем вывести, что это число случай.
Если введено число 0 или меньше или число больше 10, программа объявит результат «ложным», отрицая оператор if и вместо этого выводя, что число не находится в диапазоне от 1 до 10.
Оператор «или» (||)
Логический оператор «или» работает аналогично оператору «и» выше. Разница в том, что «или» вернет «истина», если левый или правый операнд истинен. || оператор вернет ложное значение только в том случае, если оба операнда ложны.
Рассмотрим сценарий, в котором выигрыш одного из двух счастливых чисел от 1 до 10 в игре принесет нам приз. В этом примере мы установим счастливые числа на четыре и восемь. Нам нужно написать программу на C++ для проверки победителей:
#include |
Когда пользователь приходит играть в нашу игру, его просят ввести число. Если они правильно угадывают четыре или восемь, им сообщают, что они выбрали выигрышный номер. Если пользователь вводит любое другое целочисленное значение, ему придется повторить попытку в другой раз.
Оператор «не» (!)
Логический оператор «не» используется для преобразования значения из истинного в ложное или из ложного в истинное. Точно так же, если операнд оценивается как истина, логическое «нет» заставит его оценить как ложь. Если операнд оценивается как ложный, его логический эквивалент «не» будет истинным.
В следующем примере показано одно возможное использование логического оператора «не»:
#include |
Эта программа настроена на возврат значения true в любое время, когда переменная x не равна нулю. Оператор if проверяет, равен ли x 0, что возвращает false для всех чисел, кроме нуля. ! оператор меняет результат с false на true, в результате чего программа выводит истинный результат оператора if:
Введите число: 786 Вы ввели число, отличное от 0 |
При использовании логического оператора «не» важно помнить, что он имеет очень высокий уровень приоритета в C++. Логическое «не» выполняется перед операторами сравнения, такими как «равно» (==) и «больше чем» ( > ). При кодировании с логическим «не» программист должен убедиться, что программа настроена на выполнение операторов в правильном порядке.
В приведенном ниже примере мы видим, как невыполнение этого требования приводит к возникновению проблемы:
#include |
В этом примере программа сначала выполнит логическое ! перед выполнением сравнения. При этом программа ошибочно возвращает следующее:
3 больше 11 |
Чтобы все исправить, нам нужно изменить наш оператор if следующим образом:
if (!(num1 > num2)) |
Таким образом, программа сначала выполняет реляционную операцию, а затем логическое «не», чтобы привести нас к правильному выводу.
Таблица истинности логических операций
Каким бы обширным ни было логическое выражение, при оценке все сводится к двоичному значению истинности или ложности.
Принимая во внимание только результат операнда «a», операнда «b» и логического оператора, мы можем построить следующую таблицу, которая показывает результат данной логической операции.
а | б | а && б | а || b | !a |
true | true | true | true | false |
true | false | false | true | false |
false | false | false | неверно | верно |
неверно | верно | неверно | истина | истина |
Эта таблица дает хороший способ проверить свою работу с логическими операциями. Программа, которая не возвращает перечисленный результат для предоставленных критериев, будет иметь ошибку, которую необходимо устранить.
Битовые операторы в сравнении с логическими операторами
Битовые операторы выглядят и функционируют аналогично логическим операторам, но работают только со значениями целочисленного типа, а не с логическими значениями. Побитовые операторы сравнивают два целых числа побитно и выводят 1 или 0 в зависимости от результата операции.
Для сравнения, побитовое «и» (&) очень похоже на логическое «и» (&&). Аналогично, побитовое «или» (|) следует тому же соглашению, что и логическое «или» (||). К счастью, побитовое «не» (~) выглядит существенно иначе, чем логическое «не» (!).
Смешение этих операторов приведет к ошибкам компиляции в вашей программе.
Изучайте C++ с помощью Udacity
Теперь, когда вы лучше понимаете логические операторы, вы готовы взяться за новые возможности C++.
В Udacity мы предлагаем интерактивную программу под руководством экспертов, которая выводит начинающих разработчиков C++ на новый уровень. Вы даже проверите свои навыки, написав пять реальных проектов.
Зарегистрируйтесь в нашей программе C++ Nanodegree уже сегодня!
Почему логические операторы в C не оценивают выражение целиком, когда в этом нет необходимости?
спросил
Изменено 6 лет, 6 месяцев назад
Просмотрено 2к раз
Я читал учебник для своего класса компьютерной архитектуры и наткнулся на это утверждение.
Второе важное различие между логическими операторами ‘
&&
‘ и ‘||
‘ по сравнению с их аналогами на уровне битов ‘&
‘ и ‘|
‘ заключается в том, что логические операторы не оценивают свой второй аргумент, если результат выражения может быть определен путем вычисления первого аргумента. Так, например, выражениеa && 5/a
никогда не вызовет деление на ноль, а выражениеp && *p++
никогда не вызовет разыменование нулевого указателя. (Компьютерные системы: взгляд программиста Брайанта и О’Халларона, 3-е издание, стр. 57)
Мой вопрос: почему логические операторы в C ведут себя так? Используя пример автора a && 5/a
, не нужно ли C вычислять выражение целиком, потому что &&
требует, чтобы оба предиката были истинными? Без ограничения общности мой же вопрос относится и ко второму его примеру.
- c
- логические операторы
- короткое замыкание
2
Короткое замыкание — это улучшение производительности, которое оказывается полезным для других целей.
Вы говорите: «Разве C не должен вычислять выражение целиком, потому что &&
требует, чтобы оба предиката были истинными?» Но подумайте об этом. Если левая часть &&
ложна, имеет ли значение, что оценивает правая часть? false && true
или false && false
, результат тот же: false.
Таким образом, когда левая часть &&
определена как ложная или левая часть ||
определяется как истина, значение справа не имеет значения и может быть пропущено. Это делает код быстрее, устраняя необходимость оценки потенциально дорогостоящего второго теста. Представьте, если бы правая часть вызывала функцию, которая просматривала бы весь файл в поисках заданной строки? Разве вы не хотели бы, чтобы этот тест был пропущен, если первый тест означал, что вы уже знаете комбинированный ответ?
C решил не ограничиваться гарантией короткого замыкания, а гарантировать порядок оценки, потому что это означает, что возможны тесты на безопасность, подобные предоставленному вами. Пока тесты являются идемпотентными или побочные эффекты должны возникать только при отсутствии короткого замыкания, эта функция желательна.
4
Типичным примером является проверка нулевого указателя:
if(ptr != NULL && ptr->value) { .... }
Без оценки короткого замыкания это вызвало бы ошибку при разыменовании нулевого указателя.
Сначала программа проверяет левую часть ptr != NULL
. Если это оценивается как false
, ему не нужно оценивать вторую часть, потому что уже ясно, что результатом будет false
.
3
В выражении X && Y
, если X
оценивается как false
, то мы знаем, что X && Y
всегда будет false
, каким бы ни было значение Y
. Следовательно, нет необходимости оценивать Y
.
Этот трюк используется в вашем примере, чтобы избежать деления на 0
. Если a
оценивается как false
(т. е. a == 0
), то мы не оцениваем 5/a
.
Это также может сэкономить много времени. Например, при вычислении f() && g()
, если вызов g()
обходится дорого, а f()
возвращает false
, отсутствие оценки g()
является хорошей функцией.
не нужно ли C вычислять выражение целиком, потому что && требует, чтобы оба предиката были истинными?
Ответ: Нет. Зачем работать больше, когда ответ известен «уже»?
В соответствии с определением логического оператора И, цитируя C11
, глава §6.5.14
Оператор
&&
должен дать1
, если оба его операнда не равны 0; в противном случае это дает 0,
Следуя этой аналогии, для выражения формы a && b
, в случае, если a
оценивается как FALSE, независимо от результата оценки b
, результат будет FALSE. В любом случае не нужно тратить машинный цикл на проверку b
, а затем возвращать FALSE.
То же самое относится и к логическому оператору ИЛИ, если первый аргумент оценивается как ИСТИНА, условие возвращаемого значения уже найдено и нет необходимости оценивать второй аргумент.
Это просто правило, и оно очень полезно. Возможно, поэтому это правило. Это означает, что мы можем писать более понятный код. В качестве альтернативы использование операторов if
приведет к созданию гораздо более подробного кода, поскольку вы не можете использовать операторы if
непосредственно в выражениях .
Вы уже привели один пример. Еще что-то вроде if (a && b / a)
для предотвращения целочисленного деления на ноль, поведение которого undefined в Си. .
Очень редко, если вам всегда нужно вычислять оба аргумента (возможно, они вызывают функции с побочными эффектами), вы всегда можете использовать и
и |
.