В каждом учебнике по C / C ++ вы найдете таблицу приоритетов операторов и ассоциативности, например:
http://en.cppreference.com/w/cpp/language/operator_precedence
Один из вопросов о StackOverflow задавался примерно так:
В каком порядке выполняются следующие функции:
f1() * f2() + f3();
f1() + f2() * f3();
Ссылаясь на предыдущий график, я уверенно ответил, что функции имеют ассоциативность слева направо, поэтому в предыдущих утверждениях они оценивались следующим образом:
f1 () -> f2 () -> f3 ()
После оценки функций вы завершаете оценку следующим образом:
(a1 * a2) + a3
а1 + (а2 * а3)
К моему удивлению, многие люди говорили мне, что я совершенно не прав. Решив доказать, что они не правы, я решил обратиться к стандарту ANSI C11. Я был еще раз удивлен, обнаружив, что очень мало упоминается о приоритетности операторов и ассоциативности.
Приоритет оператора определяется в соответствующем стандарте. Стандарты для C и C ++ — это одно истинное определение того, что такое C и C ++. Так что, если вы посмотрите внимательно, детали есть. На самом деле, детали в грамматика языка. Например, посмотрите на правило производства грамматики для +
а также -
в C ++ (вместе, Аддитивные-выражения):
additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression
Как видите, мультипликативный выражение является подчиненным Добавка-выражение. Это означает, что если у вас есть что-то вроде x + y * z
, y * z
выражение является подвыражением x + y * z
, Это определяет старшинство между этими двумя операторами.
Мы также можем видеть, что левый операнд Добавка-выражение расширяется к другому Добавка-выражение, что означает, что с x + y + z
, x + y
является подвыражением этого. Это определяет ассоциативность.
Ассоциативность определяет, как будут сгруппированы смежные применения одного и того же оператора. Например, +
является ассоциативным слева направо, что означает, что x + y + z
будет сгруппирован так: (x + y) + z
,
Не принимайте это за порядок оценки. Нет абсолютно никаких причин, почему значение z
не может быть вычислено раньше x + y
является. Важно то, что это x + y
это вычисляется, а не y + z
,
Для оператора вызова функции ассоциативность слева направо означает, что f()()
(что может произойти, если f
возвращает указатель на функцию, например) сгруппирован так: (f())()
(конечно, другое направление не имело бы никакого смысла).
Теперь давайте рассмотрим пример, на который вы смотрели:
f1() + f2() * f3()
*
оператор имеет более высокий приоритет, чем +
оператор, поэтому выражения сгруппированы так:
f1() + (f2() * f3())
Мы даже не должны здесь рассматривать ассоциативность, потому что у нас нет одного и того же оператора рядом друг с другом.
Оценка выражений вызова функций, однако, совершенно не упорядочена. Нет никаких причин f3
не может быть вызван первым, затем f1
, а потом f2
, Единственным требованием в этом случае является то, что операнды оператора оцениваются раньше, чем оператор. Так что это будет означать f2
а также f3
должны быть вызваны до *
оценивается и *
должны быть оценены и f1
должен быть вызван до +
оценивается.
Однако некоторые операторы накладывают последовательность на оценку своих операндов. Например, в x || y
, x
всегда оценивается раньше y
, Это позволяет для короткого замыкания, где y
не нужно оценивать, если x
как известно, уже true
,
Порядок оценки был ранее определен в C и C ++ с использованием последовательность точек, и оба изменили терминологию, чтобы определить вещи с точки зрения последовательность перед отношения. Для получения дополнительной информации см. Неопределенные Точки Поведения и Последовательности.
Приоритет операторов в стандарте C указывается синтаксисом.
(C99, 6.5p3) «Группировка операторов и операндов указывается синтаксисом. 74)»
74) «Синтаксис определяет приоритет операторов при вычислении выражения»
Обоснование C99 также говорит
«Правила приоритета закодированы в синтаксических правилах для каждого оператора».
а также
«Правила ассоциативности аналогично кодируются в синтаксических правилах».
Также обратите внимание, что ассоциативность не имеет ничего общего с порядком оценки. В:
f1 () * f2 () + f3 ()
вызовы функций оцениваются в любом порядке. Синтаксические правила Си говорят, что f1() * f2() + f3()
средства (f1() * f2()) + f3()
но порядок вычисления операндов в выражении не указан.
Один из способов думать о приоритете и ассоциативности — представить, что язык допускает только операторы, содержащие присваивание и один оператор, а не несколько операторов. Так что заявление вроде:
a = f1() * f2() + f3();
не допускается, так как имеет 5 операторов: 3 вызова функций, умножение и сложение. На этом упрощенном языке вы должны будете назначить все для временных фигур, а затем объединить их:
temp1 = f1();
temp2 = f2();
temp3 = temp1 * temp2;
temp4 = f3();
a = temp3 + temp4;
Ассоциативность и приоритет определяют, что последние два оператора должны выполняться в этом порядке, поскольку умножение имеет более высокий приоритет, чем сложение. Но это не определяет относительный порядок первых 3 утверждений; было бы так же правильно сделать:
temp4 = f3();
temp2 = f2();
temp1 = f1();
temp3 = temp1 * temp2;
a = temp3 + temp4;
sftrabbit привел пример, в котором актуальна ассоциативность операторов вызова функций:
a = f()();
При упрощении, как указано выше, это становится:
temp = f();
a = temp();
Приоритет и ассоциативность определены в стандарте, и они решают, как построить синтаксическое дерево. Приоритет работает по типу оператора (1+2*3
является 1+(2*3)
и не (1+2)*3
) и ассоциативность работает по позиции оператора (1+2+3
является (1+2)+3
и не 1+(2+3)
).
Порядок оценки другой — он не определяет, как построить синтаксическое дерево — он определяет, как evaluate
узлы операторов в синтаксическом дереве. Порядок оценки определен, чтобы не быть определенным — вы никогда не можете положиться на него, потому что компиляторы свободны выбирать любой порядок, который они считают подходящим. Это сделано для того, чтобы компиляторы могли попытаться оптимизировать код. Идея состоит в том, что программисты пишут код, который не должен зависеть от порядка вычисления, и дают одинаковые результаты независимо от порядка.
Ассоциативность слева направо означает, что f() - g() - h()
средства (f() - g()) - h()
, ничего более. предполагать f
возвращается 1
, предполагать g
возвращается 2
, предполагать h
возвращается 3
, Ассоциативность слева направо означает, что результат (1 - 2) - 3
, или же -4
: компилятору все еще разрешено первый вызов g
а также h
, что не имеет ничего общего с ассоциативностью, но не может дать результат 1 - (2 - 3)
, что было бы что-то совершенно другое.