Приоритет логических операторов в 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

Здесь есть три вопроса:

  1. Порядок приоритетности.
  2. Порядок оценки.
  3. Короткое замыкание логических операторов.

Порядок приоритетности подразумевает, что ++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.


Для лучшего понимания просто попробуйте изменить ++aa++ в выражении.
Оператор приращения post также увеличивает значение операнда на 1 но значение выражения является исходным значением операнда до операции инкремента. Таким образом, a++ будет оценен до 0 и из-за поведения короткого замыкания выражение правой части || оператор ++b && ++c) будет оценен.

Операция логического И (expr1 && expr2) также использует короткое замыкание. При логическом коротком замыкании второй операнд expr2 оценивается только тогда, когда результат не полностью определяется первым операндом, expr1. То есть expr2 будет оцениваться только в том случае, если expr1 является логическим 1 (true) а ++b приведет к 1. Итак, если вы это сделаете

a++ || ++b && ++c;
^^^

Выход будет - 1 1 1.