Приоритет и порядок оценки

Приоритет и ассоциативность операторов C влияют на группировку и вычисление операндов в выражениях. Приоритет оператора имеет значение только в том случае, если рядом стоят другие операторы с более высоким или более низким приоритетом. Выражения с операторами с наивысшим приоритетом вычисляются первыми. Приоритет также можно описать с помощью термина "привязка". Про операторы с более высоким приоритетом говорят, что они имеют более тесную привязку.

В следующей таблице указывается приоритет и ассоциативность (порядок вычисления операндов) операторов C, которые перечислены в порядке убывания приоритета. Если в одной строке отображается несколько операторов, они имеют равный приоритет и вычисляются с учетом их ассоциативности. Операторы, перечисленные в этой таблице, рассматриваются в разделе Постфиксные операторы. В остальной части этого раздела приводятся общие сведения о приоритете и ассоциативности.

Приоритет и ассоциативность операторов C

Символ 1 Тип операции Ассоциативность
[ ] ( ) . ->
++-- (постфикс)
Выражение Слева направо
sizeof & * + - ~ !
++-- (префикс)
Унарный Справа налево
typecasts Унарный Справа налево
* / % Мультипликативный Слева направо
+ - Аддитивный Слева направо
<< >> Побитовый сдвиг Слева направо
< > <= >= Реляционный Слева направо
== != Равенство Слева направо
& Побитовое И Слева направо
^ Побитовое исключающее ИЛИ Слева направо
| Побитовое включающее ИЛИ Слева направо
&& Логическое И Слева направо
|| Логическое ИЛИ Слева направо
? : Условное выражение Справа налево
= *= /= %=
+= -= <<= >>= &=
^= |=
Простое и составное присваивание 2 Справа налево
, Последовательное вычисление Слева направо

1 Операторы перечислены в порядке убывания приоритета. Если в одной строке или группе находится несколько операторов, они имеют равный приоритет.

2 Все операторы простого и составного присваивания имеют равный приоритет.

Выражение может содержать несколько операторов с равным приоритетом. Если несколько таких операторов находятся в выражении на одном уровне, вычисление выполняется согласно их ассоциативности (либо справа налево, либо слева направо). Направление вычисления не влияет на результаты выражений, в которых на одном и том же уровне находится более одного оператора умножения (*) или сложения (+) либо бинарного побитового оператора (&, | или ^). Порядок операций не определен языком. Если компилятор может гарантировать согласованный результат, то он может вычислять такие выражения в любом порядке.

Только операторы последовательного вычисления (,), логический оператор AND (&&), логический оператор OR (||), операторы условных выражений (? :) и операторы вызова функции создают точки следования и тем самым гарантируют определенный порядок вычисления своих операндов. Оператор вызова функции представляет собой пару скобок, следующих за идентификатором функции. Оператор последовательного вычисления (,) гарантирует, что его операнды будут вычисляться слева направо (оператор-запятая в вызове функции не является оператором последовательного вычисления и не предоставляет таких гарантий). Дополнительные сведения см. в статье Точки следования C.

Логические операторы также гарантируют вычисление своих операндов слева направо. Однако они вычисляют наименьшее количество операндов, необходимое для определения результата выражения. Это называется "сокращенным вычислением". Таким образом, некоторые операнды в выражении могут не вычисляться. Например, в выражении:

x && y++

второй операнд, y++, вычисляется, только если x имеет значение true (не равно нулю). Таким образом, если y дает значение false (0), то x не увеличивается.

Примеры

Ниже приводится несколько примеров автоматической привязки выражений компилятором:

Выражение Автоматическая привязка
a & b || c (a & b) || c
a = b || c a = (b || c)
q && r || s-- (q && r) || s--

В первом выражении оператор побитового И (&) имеет более высокий приоритет, чем оператор логического ИЛИ (||), поэтому a & b формирует первый операнд операции логического ИЛИ.

Во втором выражении оператор логического ИЛИ (||) имеет более высокий приоритет, чем оператор простого присваивания (=), поэтому b || c группируется как правый операнд присваивания. Обратите внимание, что операнду a присваивается значение 0 или 1.

В третьем примере приводится правильно сформированное выражение, которое может дать непредвиденный результат. Оператор логического И (&&) имеет более высокий приоритет, чем оператор логического ИЛИ (||), поэтому q && r становится одним операндом. Так как логические операторы гарантируют вычисление операндов слева направо, то операция q && r вычисляется раньше, чем s--. Но если выражение q && r имеет ненулевое значение, то s-- не вычисляется и s не уменьшается. Если то, что значение s не будет уменьшено, может вызвать проблемы в вашей программе, вы можете либо поставить s-- первым операндом в выражении, либо выполнить декремент s в отдельной операции.

Следующее выражение недопустимо и приводит к созданию диагностического сообщения во время компиляции.

Недопустимое выражение Группировка по умолчанию
p == 0 ? p += 1: p += 2 ( p == 0 ? p += 1 : p ) += 2

В этом выражении оператор равенства (==) имеет наибольший приоритет, поэтому выражение p == 0 становится одним операндом. Далее по приоритету следует оператор условного выражения (? :). Его первым операндом является p == 0, а вторым — p += 1. Однако последним операндом оператора условного выражения считается не p, а p += 2, поскольку в этом случае p имеет более тесную привязку к оператору условного выражения, чем к оператору составного присваивания. Синтаксическая ошибка возникает потому, что операция += 2 не имеет левого операнда. Для того чтобы избежать ошибок такого рода и сделать код более читаемым, необходимо использовать скобки. Так, предыдущий пример можно исправить при помощи круглых скобок, как показано ниже:

( p == 0 ) ? ( p += 1 ) : ( p += 2 )

См. также

Операторы в C