Приоритет логических операторов в C
#include <stdio.h>
int main(void) {
int a = 0, b = 0, c = 0;
++a || ++b && ++c;
printf("%d %d %d", a, b, c);
return 0;
}
Выходы 1
, 0
, 0
по gcc 8.1.0. Приоритет &&
должен быть выше, чем ||
,
Почему b
и c
все еще 0
?
Ответы
Ответ 1
Здесь есть три вопроса:
- Порядок приоритетности.
- Порядок оценки.
- Короткое замыкание логических операторов.
Порядок приоритетности подразумевает, что ++a || ++b && ++c
++a || ++b && ++c
оценивается как ++a || (++b && ++c)
++a || (++b && ++c)
.
Однако из-за требований к коротким замыканиям логических операторов ++a
оценивается ++a
. Только если это значение (++b && ++c)
false
будет оценено (++b && ++c)
. В вашем случае ++a
оценивается как true
. Следовательно, (++b && ++c)
никогда не оценивается.
Ответ 2
Выражение ++a || ++b && ++c
++a || ++b && ++c
сгруппирован как ++a || (++b && ++c)
++a || (++b && ++c)
. Но, правая часть ||
оценивается только в том случае, если ++a
равно 0
, а это не так.
Ответ 3
Логический оператор OR ||
(а также логический оператор AND &&
) является одним из немногих операторов, выполняющих короткую операцию обращения.
В разделе 6.5.14 стандарта C говорится следующее о логическом операторе OR:
4 В отличие от побитового |
оператора, ||
оператор гарантирует оценку слева направо; если второй операнд оценивается, то существует точка последовательности между оценками первого и второго операндов. Если первый операнд сравнивается не равным 0, второй операнд не оценивается.
Поскольку ++a
оценивается как 1, результат ||
оператору гарантировано 1, а правая сторона не оценивается. Кроме того, поскольку &&
имеет более высокий приоритет, чем ||
, правая часть ||
оператор ++b && ++c
, что означает, что ни ++b
ни ++c
не оцениваются.
Ответ 4
Приоритет контролирует только то, как анализируются выражения, а не как они оцениваются. Арифметика *
имеет более высокий приоритет, чем +
, поэтому a * b + c
анализируется как (a * b) + c
. Однако каждый из a
, b
и c
может быть оценен в любом порядке. Результат a * b
должен быть известен, прежде чем он может быть добавлен к результату c
, но это не означает, что a * b
необходимо оценить до c
.
Во-вторых, в отличие от большинства операторов в C, ||
и &&
операторы &&
оценку слева направо. Выражение вроде a || b && c
a || b && c
будет анализироваться как a || (b && c)
a || (b && c)
, но a
всегда будет оценен a
, а b && c
будет оцениваться только в том случае, если результат a
равен 0.
Ответ 5
Что касается приоритета, то x || y && z
x || y && z
действует так же, как x + y * z
: второй оператор связывается более плотно, чем первый, и эти выражения эквивалентны x || (y && z)
x || (y && z)
и x + (y * z)
соответственно.
Причина, по которой b
и c
в вопросе не увеличиваются, заключается в том, что в дополнение к приоритету короткое замыкание логических операций: после того, как вы получили достаточно далеко, чтобы узнать результат, остальная часть выражения пропущена. Оба ||
и &&
оценивают их аргументы слева направо, поэтому в a() || b()
a() || b()
и в a() && b()
вызов функции a()
встречается перед вызовом b()
.
В простых случаях, если a()
возвращает true
, то в выражении a() || b()
a() || b()
вызов b()
не будет выполнен, потому что это не повлияет на результат. Аналогично, если a()
возвращает false
, то в выражении a() && b()
вызов b()
не будет выполнен.
В коде в примере приращения к b
и c
не будут выполняться, потому что ++a
создает ненулевое значение, поэтому результат выражения является true
без необходимости оценивать что-либо после ++a
.
Ответ 6
Приоритет операторов не имеет ничего общего с порядком оценки. Приоритет является приоритетом для группировки различных типов операторов с их операндами.
Итак, выражение
++a || ++b && ++c;
будет оцениваться как
++a || (++b && ++c);
Логический И и Логический оператор ИЛИ представляют собой точки последовательности и, следовательно, гарантируют конкретный порядок оценки для их операндов слева направо.
Порядок оценки:
заказ
......
- Если имеется точка последовательности между подвыражениями E1 и E2, то вычисляются как вычисление значения, так и побочные эффекты E1 - перед каждым вычислением значения и побочным эффектом E2
правила
...
2) После оценки первого (левого) операнда есть точка последовательности и перед оценкой второго (правого) операнда следующих двоичных операторов: && (логическое И), || (логическое ИЛИ) и (запятая).
Операция логического ИЛИ (expr1 || expr2)
использует короткое замыкание. То есть expr2
не оценивается, если expr1
является логическим 1
(true)
.
Начальное значение a
, b
и c
равно 0
. В выражении:
++a || ++b && ++c;
++a
→ pre-increment a
.
Это означает, что значение выражения ++a
приводит к увеличению значения a
которое будет равно 1
. Так как ||
оператор использует поведение короткого замыкания, выражение правой стороны ||
не будет оцениваться. Следовательно, вы получаете выход - 1 0 0
.
Для лучшего понимания просто попробуйте изменить ++a
→ a++
в выражении.
Оператор приращения post также увеличивает значение операнда на 1
но значение выражения является исходным значением операнда до операции инкремента. Таким образом, a++
будет оценен до 0
и из-за поведения короткого замыкания выражение правой части ||
оператор ++b && ++c
) будет оценен.
Операция логического И (expr1 && expr2
) также использует короткое замыкание. При логическом коротком замыкании второй операнд expr2
оценивается только тогда, когда результат не полностью определяется первым операндом, expr1
. То есть expr2
будет оцениваться только в том случае, если expr1
является логическим 1
(true)
а ++b
приведет к 1
. Итак, если вы это сделаете
a++ || ++b && ++c;
^^^
Выход будет - 1 1 1
.