Меняется ли порядок операций в выражении if? - PullRequest
31 голосов
/ 22 июля 2011

Я недавно столкнулся с чем-то, что, как мне показалось, я сразу понял, но, размышляя об этом больше, мне хотелось бы понять, почему это работает так, как работает.

Рассмотрим код ниже. (x-- == 9) явно оценивается, а (y++ == 11) - нет. Моей первой мыслью было, что логический && начинает работать, видит, что выражение уже стало ложным, и начинает работать, прежде чем оценивать вторую часть выражения.

Чем больше я об этом думаю, тем больше я не понимаю, почему это ведет себя так, как есть. Насколько я понимаю, логические операторы опускаются ниже операций приращения в порядке приоритета. Не следует ли оценивать (y++ == 11), даже если общее выражение уже стало ложным?

Другими словами, разве порядок операций не должен диктовать, что (y++ == 11) должен быть оценен до того, как оператор if поймет, что выражение в целом будет ложным?

#include <iostream>
using namespace std;

int main( int argc, char** argv )
{
    int x = 10;
    int y = 10;

    if( (x-- == 9) && (y++ == 11) )
    {
        cout << "I better not get here!" << endl;
    }

    cout << "Final X: " << x << endl;
    cout << "Final Y: " << y << endl;
    return 0;
}

Выход:

Final X: 9
Final Y: 10

Ответы [ 6 ]

49 голосов
/ 22 июля 2011

логические операторы опускаются ниже операций приращения в порядке старшинство.

Порядок старшинства не порядок исполнения. Это совершенно разные понятия. Порядок приоритета влияет только на порядок выполнения в той степени, в которой операнды оцениваются перед их оператором, а порядок приоритета помогает определить, какие операнды есть у каждого оператора.

Операторы короткого замыкания являются частичным исключением даже из правила, согласно которому операнды оцениваются перед оператором, так как они оценивают LHS, то оператор может сказать, следует ли оценивать RHS, возможно RHS оценивается, затем вычисляется результат оператора.

Не думайте, что операции с более высоким приоритетом "выполняются первыми". Подумайте о них "крепче". ++ имеет более высокий приоритет, чем &&, и в выражении x ++ && y ++ приоритет оператора означает, что ++ "более тесно связывается" с y, чем &&, и поэтому общее выражение эквивалентно (x++) && (y++), а не (x++ && y) ++.

45 голосов
/ 22 июля 2011

Не следует оценивать (y++ == 11), даже если общее выражение уже стало false?

Нет: операторы && и || замкнуты: они оцениваются слева направо, и как только результат выражения известен, оценка останавливается (то есть, как только известно, что выражение равно false в случае серии &&,или true в случае серии ||) (*) .

Нет смысла выполнять дополнительную работу, которая не требуется выполнять.Такое короткое замыкание также весьма полезно и позволяет писать более короткий код.Например, учитывая указатель на объект типа структуры, вы можете проверить, является ли указатель нулевым, а затем разыменовать указатель в последующем подвыражении, например: if (p && p->is_set) { /* ... */ }.


(*)Обратите внимание, что в C ++ вы можете перегрузить как &&, так и || для операндов типа класса, и если вы это сделаете, они теряют свойство короткого замыкания (обычно нежелательно перегружать && и || дляэто причина).

13 голосов
/ 22 июля 2011

Приоритет и ассоциативность do not указывают порядок, в котором фактически выполняются операции. Они указывают, как сгруппированы операции: в следующем выражении:

x && y++

... нижний приоритет && говорит, что он сгруппирован так, как если бы он был:

x && (y++)

, а не

(x && y)++

В вашем выражении относительный приоритет && и ++ не имеет значения, потому что вы все равно разделяете эти операторы скобками.

Группировка (и, следовательно, приоритет и ассоциативность) указывает какое значение используется каждым оператором; но он ничего не указывает о , когда делает это.

Для большинства операторов порядок, в котором выполняются операции, не определен - однако, в случае && указывается сначала для вычисления левого операнда, а затем только для правого операнда, если результат левый операнд был ненулевым.

7 голосов
/ 22 июля 2011

Нет. Порядок приоритета просто решает, получите ли вы это:

A && B

(A = 1005 * и B y++ == 11) или

A == B == C

A, равным x--, B, являющимся 9 && y++, и C, равным 11).

Очевидно, мы имеем дело с первым случаем. Короткое замыкание полностью применяется; если A истинно, B не оценивается.

1 голос
/ 22 июля 2011

Условные операторы оценивают слева направо и останавливаются, как только известен результат (И с ложным или ИЛИ с истинным значением).

0 голосов
/ 22 июля 2011
Стандарт

C не предписывает какой-либо конкретный порядок вычисления выражений в if. Таким образом, поведение будет зависеть от компилятора и использовать этот стиль кодирования не переносимо. Вы сталкиваетесь с этой проблемой, потому что увеличение / уменьшение значения является постоперацией, но стандарт говорит как постоперация выражения, где используется переменная. Так что, если компилятор считает, что ваше выражение - это просто использование одной переменной как x или y, то вы увидите один результат. Если компилятор считает, что выражение является целым, если вычисление выражения, то вы увидите другой результат. Надеюсь, это поможет.

...