Почему "++ x || ++ y && ++ z" сначала вычисляет "++ x", хотя оператор "&&" имеет более высокий приоритет, чем "||"
Почему ++x || ++y && ++z
сначала вычисляет ++x
, хотя приоритет оператора &&
выше, чем ||
?
Ответы
Ответ 1
Отмотать, R, а другие объяснили, что на самом деле происходит. Поэтому позвольте мне добавить:
Посылка вашего вопроса ошибочна. Тот факт, что &&
имеет более высокий приоритет, не означает, что окружающие его операнды должны быть оценены перед любыми операндами в выражении с более низким приоритетом. Даже если короткое замыкание в частном случае ||
и &&
не обязательно будет таким.
Например, рассмотрим a=b+c+d*e
; *
имеет более высокий приоритет, чем +
, но это не означает, что d*e
необходимо оценить до b+c
. Это просто означает, что он должен быть оценен до того, как мы добавим продукт в выражение в целом. Компилятор мог оценить это выражение как temp1=d*e
, temp2=b+c
, a=temp1+temp2
или он мог бы оценить temp1=b+c
, temp2=d*e
, a=temp1+temp2
. Оба будут одинаково действительны.
При коротком замыкании поведения ||
и &&
на порядок оценки есть некоторые дополнительные ограничения.
В качестве побочного примечания: В общем, я бы избегал писать такой код. Я могу легко увидеть, как другой программист пытается прочитать этот код, запутавшись, только когда приращения произойдут, а когда они этого не сделают. Ну, может быть, если бы вы использовали имена реальных переменных, это выглядело бы не так странно.
Я иногда полагаюсь на короткое замыкание, предотвращающее побочные эффекты. Как
if (!eof() && readNextInt()>0)
Я полагаюсь на короткое замыкание, чтобы предотвратить чтение, если мы уже в конце файла, Или
if (confirmDelete==YES && deleteEntry()!=-1)
Я полагаюсь на первый тест на короткое замыкание на false, поэтому я не делаю удаления, когда не должен. Но эти примеры кажутся мне довольно прямыми, я надеюсь, что любой компетентный программист увидит, что я делаю. Но когда примеры становятся загадочными, я думаю, что их нужно разбить. Рассмотрим
if (customerType==RETAIL || lastDepositAmount()>100.00)
Если lastDepositAmount()
имел побочный эффект, то если customerType
является розничным, этот побочный эффект никогда не произойдет. Я не думаю, что это обязательно было бы очевидно для читателя. (Отчасти потому, что имя функции подразумевает, что оно извлекает данные, а не выполняет какое-либо обновление, а отчасти потому, что между типом клиента и объемом депозита нет очевидной связи - это звучит как две независимые вещи.) По общему признанию, это субъективны. Но когда вы сомневаетесь, выберите простоту и ясность в отношении тривиального улучшения производительности. Всегда выбирайте простоту и ясность над тем, "эй, это отличный способ использования неясной функции, любой, кто читает это, будет впечатлен тем, насколько я умный, чтобы понять язык достаточно хорошо, чтобы сделать это".
Ответ 2
А?
Если вы говорите, что &&
связывается более жестко, чем ||
(который является истинным), выражение затем эквивалентно
++x || (++y && ++z)
Так как ||
коротких замыканий, сначала нужно оценить левую сторону.
Если вы имеете в виду, что он должен быть эквивалентен
(++x || ++y) && ++z
То же самое верно, так как &&
также является коротким замыканием, что означает, что сначала нужно оценить значение ||
, что, в свою очередь, делает ++x
первое, что нужно оценить.
Ответ 3
Так как &&
имеет более высокий приоритет, чем ||
, это означает, что ваше выражение эквивалентно:
++x || (++y && ++z)
Таким образом, прежде чем программа даже начнет оценивать результат &&
, она должна оценить ++x
, чтобы определить, должен ли быть оценен правый операнд ||
(логические двоичные операторы "короткозамкнуты" ", что означает, что они не оценивают правую часть, если левая часть достаточна для определения результата). Здесь нет ничего логичного или компилятора. Поведение этого выражения точно задается стандартом C и будет одинаковым для любого компилятора. Было бы даже работать, если x
, y
и z
были одинаковой переменной, так как ||
и &&
вводят точки последовательности.
Ответ 4
Здесь есть ужасно неправильные ответы.
Порядок оценки этого выражения не зависит от реализации. Он четко определен!
Так как &&
связывается выше ||
, это выражение эквивалентно ++x || (++y && ++z)
. Кроме того, как &&
, так и ||
закорочены, поэтому, если левая часть выражения достаточно для определения значения, правая часть оценивается никогда.
Когда это так? Ну, мы знаем, что False && a
всегда решает False
, независимо от значения a
, в частности, даже если a
не является единственным значением, а вместо этого сложным выражением. Поэтому мы вообще не оцениваем a
. Аналогично, True || a
всегда разрешается True
.
Это приводит к очень определенному порядку оценки в этом примере: сначала ++x
, затем (если вообще) ++y
и затем (опять же: если вообще) ++z
.
Ответ 5
Операторы оцениваются с использованием польской древоструктуры дерева, как на диаграмме, и это алгоритм оценки глубины.
Пусть даются имена для операций:
A = ++ x
B = ++ y
C = ++ z
D = B && С
E = C || D
Порядок оценки: A, B, C, D, E.
Конечно, C имеет оптимизацию, и если A истинно, тогда E будет оцениваться как истина без оценки B C и D. Аналогично, если B является ложным, то C не будет оцениваться для оптимизации скорости оценки.
![alt text]()
Ответ 6
++ имеет наивысший приоритет, так как его pre. Это означает, что значение результата этой операции передается двоичным операторам. Однако интересная вещь - короткое замыкание ++ x or'ed с остальными. Если ++ x истинно, то остальные могут вообще не выполняться.
Ответ 7
Приоритет и порядок оценки - это не одно и то же, особенно в этом случае. Все приоритеты говорят вам, что выражение должно анализироваться как
++x || (++y && ++z)
IOW, это означает, что выражения OR'd вместе являются ++x
и (++y && ++z)
. Это не означает, что (++y && ++z)
будет оцениваться до ++x
.
Для логических операторов ||
и &&
оценка всегда слева направо (Online C Standard, разделы 6.5.13 и 6.5.14). Для любого выражения
a || b
a
будет полностью оценена; b
не будет оцениваться, если результат a
равен 0 (подумайте о a
для ++x
и b
для ++y && ++z
). Аналогично, для любого выражения
a && b
a
будет полностью оценена; b
не будет оцениваться, если результат a
не равен 0.
Итак, для вашего случая сначала оценивается выражение ++x
. Если результат равен 0, только тогда будет оценен ++y && ++z
.
||
и &&
являются особенными в том, что порядок оценки гарантированно остается слева направо. Это не относится к поразрядным или арифметическим операторам. Например, в выражении
++x + ++y * ++z
выражения ++x
, ++y
и ++z
могут быть оценены в любом порядке; все приоритеты говорят вам, что результат (y+1) * (z+1)
будет добавлен к результату x+1
.
Ответ 8
Лучшие два ответа объясняют это довольно хорошо... Я бы отметил, что || "OrElse".
Правая сторона не затрагивается, если левая сторона верна. Левая сторона && (AndAlso) не затрагивается, если правая сторона неверна.
Если вы хотите, чтобы обе стороны увеличивались, используйте | и &.
http://msdn.microsoft.com/en-us/library/2h9cz2eb(v=VS.80).aspx
Логическая операция называется короткое замыкание, если скомпилированный код может обойти оценку одного выражение в зависимости от результата другое выражение. Если результат первое оцениваемое выражение определяет конечный результат эксплуатации, нет необходимости оценить второе выражение, потому что он не может изменить окончательный результат. Короткое замыкание может улучшить производительность, если обходное выражение является сложным, или если оно связано с вызовы процедур.
В MSDN есть таблицы, показывающие, как части "(не оцениваются)".
Зачем прикасаться к части 2, если Часть 1 предварительно определяет результат детерминированным образом?
Ответ 9
Прошло много времени с тех пор, как я работал с C, но если я правильно помню, ++ является унарным оператором, который всегда имеет прецедент над двоичными операторами. Имеет смысл, если вы думаете об этом.
Ответ 10
Вы не должны полагаться на воспринимаемый приоритет оператора, если ваши операции чувствительны к порядку, поскольку разные компиляторы (и разные версии одного и того же) могут реализовать приоритеты по-разному.
В любом случае b определение ++ x означает, что x должен быть увеличен до того, как он используется, и поэтому вы ожидаете, что ему будет присвоен высокий приоритет.
Ответ 11
Нет нет нет
if (++x || ++y && ++z) { /* ... */ }
Да
++x;
++y;
++z;
if (x || (y && z)) { /* ... */ }
Изменить после сообщений со стороны коллег:)
ДА!
if (is_data_valid(x, y, z)) process_data(&x, &y, &z);