Порядок вычисления выражения

#c #evaluation #operator-precedence

#c #вычисление #оператор -приоритет

Вопрос:

Я только что прочитал, что порядок вычисления и приоритет операторов — это разные, но связанные понятия в C . Но мне все еще неясно, чем они отличаются, но связаны ?.

 int x = c   a * b;    // 31
int y = (c   a) * b;  // 36
  

Какое отношение имеют приведенные выше утверждения к порядку вычисления. например, когда я говорю (c a) , меняю ли я порядок вычисления выражения, изменяя его приоритет?

Ответ №1:

Важная часть порядка вычисления заключается в том, имеет ли какой-либо из компонентов побочные эффекты.

Предположим, у вас есть это:

 int i = c()   a() * b();
  

Где a и b имеют побочные эффекты:

 int global = 1;

int a() {
    return global  ;
}
int b() {
    return   global;
}
int c() {
    return global * 2;
}
  

Компилятор может выбрать, в каком порядке вызывать a() , b() c() а затем вставить результаты в выражение. В этот момент приоритет берет верх и решает, в каком порядке применять операторы and * .

В этом примере наиболее вероятными результатами являются либо

  1. Компилятор c() сначала выполнит вычисление, а затем a() и затем b() , что приведет к i = 2 1 * 3 = 5
  2. Компилятор b() сначала выполнит вычисление, а затем a() и затем c() , что приведет к i = 6 2 * 2 = 10

Но компилятор волен выбирать любой порядок, который он хочет.

Короче говоря, приоритет сообщает вам порядок, в котором операторы применяются к аргументам ( * до ), тогда как порядок вычисления сообщает вам, в каком порядке разрешаются аргументы ( a() , b() , c() ). Вот почему они «разные, но связанные».

Ответ №2:

«Порядок вычисления» относится к тому, когда различные подвыражения в одном и том же выражении вычисляются относительно друг друга.

Например, в

 3 * f(x)   2 * g(x, y)
  

у вас есть обычные правила приоритета между умножением и сложением. Но у нас есть вопрос о порядке вычисления: произойдет ли первое умножение перед вторым или второе перед первым? Это важно, потому что, если f() имеет побочный эффект, который изменяет y , результат всего выражения будет отличаться в зависимости от порядка операций.

В вашем конкретном примере этот сценарий вычисления порядка (в котором результирующее значение зависит от порядка) не возникает.

Ответ №3:

Пока мы говорим о встроенных операторах: нет, вы не меняете порядок вычисления с помощью () . Вы не можете контролировать порядок вычисления. На самом деле, здесь вообще нет «порядка вычисления».

Компилятору разрешено вычислять это выражение любым желаемым способом, если результат правильный. Для вычисления этих выражений даже не требуется использовать операции сложения и умножения. Сложение и умножение существуют только в тексте вашей программы. Компилятор может полностью и полностью игнорировать эти конкретные операции. На некоторой аппаратной платформе такие выражения могут быть вычислены с помощью одной атомной машинной операции. По этой причине понятие «порядок вычисления» здесь не имеет никакого смысла. Там нет ничего, к чему вы могли бы применить концепцию «порядка».

Единственное, что вы меняете, используя () , — это математическое значение выражения. Допустим a , b и c это все 2 . a b * c Должно вычисляться до 6 , в то время (a b) * c как должно вычисляться до 8 . Вот и все. Это единственное, что вам гарантировано: результаты будут правильными. Как получены эти результаты, совершенно неизвестно. Компилятор может использовать абсолютно все, любой метод и любой «порядок вычисления», если результаты верны.

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

 int x = c   a * b;
int y = (c   a) * b;
  

компилятор может свободно оценивать их как

 int x = c   a * b;
int y = c * b   x - c;
  

что также приведет к правильному результату (при условии отсутствия проблем, связанных с переполнением). В этом случае фактическое расписание вычислений даже отдаленно не будет похоже на то, что вы написали в своем исходном коде.

Короче говоря, предполагать, что фактическая оценка будет иметь какое-либо существенное сходство с тем, что вы написали в исходном коде вашей программы, в лучшем случае наивно. Несмотря на распространенное мнение, встроенные операторы обычно не переводятся в их машинных «аналогах».

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

Ответ №4:

Ответ может быть или не быть.

Порядок вычисления a, b и c зависит от интерпретации компилятором этой формулы.

Комментарии:

1. Как указывали другие, порядок вычисления отличается от правила приоритета.

Ответ №5:

Рассмотрим приведенный ниже пример:

 #include <limits.h>
#include <stdio.h>
int main(void)
{
    double a = 1   UINT_MAX   1.0;
    double b = 1   1.0   UINT_MAX;
    printf("a=%gn", a);
    printf("b=%gn", b);
    return 0;
}
  

Здесь с точки зрения математики, какой мы ее знаем, a и b должны вычисляться одинаково и должны иметь одинаковый результат. Но верно ли это в мире C ( )? Смотрите выходные данные программы.

Комментарии:

1. Это правда… но разница значений a и b связана с интегральными правилами продвижения, а не с порядком вычисления.

2. @YSC Порядок определяет конверсии / рекламные акции.

Ответ №6:

Я хочу представить ссылку, которую стоит прочитать в связи с этим вопросом. В правилах 3 и 4 упоминается о sequence point , еще одна концепция, которую стоит запомнить.