В игре два разных, но связанных между собой термина: приоритет оператора и порядок вычисления .
приоритет оператора определяет порядок синтаксического анализа:
- В вашем выражении скобка имеет наивысший приоритет, поэтому то, что внутри, принадлежит друг другу.
- Далее у нас есть операторы вызова функции
()
.Ничего странного там нет, они постфиксные и принадлежат их оператору, имени функции. Далее у нас есть бинарные операторы +
и -
.Они принадлежат к одной группе операторов «аддитивные операторы» и имеют одинаковый приоритет.Когда это происходит, ассоциативность операторов для операторов этой группы решает, в каком порядке они должны быть проанализированы.
Для аддитивных операторов ассоциативность операторов слева направо.Это означает, что выражение гарантированно будет проанализировано как (get_1() + get_2()) - ...
.
- И, наконец, у нас есть оператор запятой странного числа с наименьшим приоритетом из всех.
Как только приоритет оператора отсортирован, как указано выше, мы знаем, какие операнды принадлежатна какие операторы.Но это ничего не говорит о том, в каком порядке будет выполняться выражение.Вот где начинается порядок оценки.
Обычно C говорит сухим стандартным языком:
За исключением случаев, указанных позднее, побочные эффекты и вычисления значений подвыражений не являются последовательными.
На простом английском языке это означает, что порядок вычисления операндов по большей части не определен, за некоторыми исключениями.
Для аддитивных операторов +
и -
, это правда.Учитывая a + b
, мы не можем знать, будет ли сначала выполняться a
или b
.Порядок оценки не определен - компилятор может выполнить его в любом удобном для него порядке, не должен документировать, как и даже не должен вести себя согласованно от случая к случаю.
Это намеренно оставлено неуказанным в стандарте C,чтобы разные компиляторы могли по-разному разбирать выражения.По сути, позволяя им сохранять свой алгоритм дерева выражений коммерческой тайной, чтобы некоторые компиляторы могли создавать более эффективный код, чем другие на свободном рынке.
И именно поэтому gcc и clang дают разные результаты.Вы написали код, который опирается на порядок оценки.Это не вина любого компилятора - мы просто не должны писать программы, которые полагаются на плохо заданное поведение.Если вам нужно выполнять эти функции в определенном порядке, вы должны разделить их на несколько строк / выражений.
Что касается оператора запятой, это один из редких особых случаев.Он поставляется со встроенной «точкой последовательности», которая гарантирует, что левый операнд всегда вычисляется (выполняется) перед правым.Другими такими частными случаями являются операторы && ||
и оператор ?:
.