运算符优先级与计算顺序

Kus*_*wal 5 c expression-evaluation operator-precedence

一位朋友让我简单地解释一下运算符优先级和求值顺序之间的区别。我是这样向他们解释的:-

让我们举个例子——

int x;
int a = 2;
int b = 5;
int c = 6;
int d = 4;

x = a * b / (c + d);
Run Code Online (Sandbox Code Playgroud)

在这里, 的最终值x将变为1c这是因为,首先将和 的值d相加( ),然后将和6+4的值相乘(),最后进行除法(),最终值变为,然后将其赋值到。ab2*510/101x

所有这些都是由运算符优先级指定的。在此示例中,括号强制加法在乘法和除法之前进行,即使加法的优先级较低。另外,乘法是在除法之前执行的,因为乘法和除法具有相同的优先级,并且两者都具有从左到右的结合性。

现在到了重要的部分,即该表达式的求值顺序。

在一个系统上,求值的顺序可能是这样的 -

/* Step 1 */   x = a * b / (c + d);
/* Step 2 */   x = a * 5 / (c + d);
/* Step 3 */   x = a * 5 / (c + 4);
/* Step 4 */   x = a * 5 / (6 + 4);
/* Step 5 */   x = a * 5 / 10;
/* Step 6 */   x = 2 * 5 / 10;
/* Step 7 */   x = 10 / 10;
/* Step 8 */   x = 1;
Run Code Online (Sandbox Code Playgroud)

请注意,在任何步骤中,始终确保维持运算符优先级,即即使在步骤 2 中b被替换5,乘法直到步骤 7 才发生。因此,即使不同系统的求值顺序不同,始终保持运算符优先级。

在另一个系统上,求值的顺序可能是这样的 -

/* Step 1 */   x = a * b / (c + d);
/* Step 2 */   x = a * b / (6 + d);
/* Step 3 */   x = a * b / (6 + 4);
/* Step 4 */   x = a * b / 10;
/* Step 5 */   x = 2 * b / 10;
/* Step 6 */   x = 2 * 5 / 10;
/* Step 7 */   x = 10 / 10;
/* Step 8 */   x = 1;
Run Code Online (Sandbox Code Playgroud)

再次,运算符的优先级得到保持。

在上面的例子中,整个行为是明确定义的。造成这种情况的原因之一是所有变量都不同。从技术角度来说,此示例中的行为是明确定义的,因为没有对任何变量进行无序的修改。因此,在任何系统上,最终x总是会被分配该值1

现在,让我们将上面的示例更改为:-

int x;
int y = 1;

x = ++y * y-- / (y + y++);
Run Code Online (Sandbox Code Playgroud)

在这里,分配给的最终值因x系统而异,从而导致行为未定义。

在一个系统上,求值的顺序可能是这样的 -

/* Step 1 */   x = ++y * y-- / (y + y++);   // (y has value 1)
/* Step 2 */   x = ++y * y-- / (1 + y++);   // (y still has value 1)
/* Step 3 */   x = ++y * 1 / (1 + y++);     // (y now has value 0)
/* Step 4 */   x = 1 * 1 / (1 + y++);       // (y now has value 1)
/* Step 5 */   x = 1 * 1 / (1 + 1);         // (y now has value 2)
/* Step 6 */   x = 1 * 1 / 2;
/* Step 7 */   x = 1 / 2;
/* Step 8 */   x = 0;
Run Code Online (Sandbox Code Playgroud)

再次,运算符的优先级得到保持。

在另一个系统上,求值的顺序可能是这样的 -

/* Step 1 */   x = ++y * y-- / (y + y++);   // (y has value 1)
/* Step 2 */   x = ++y * y-- / (y + 1);     // (y now has value 2)
/* Step 3 */   x = ++y * 2 / (y + 1);       // (y now has value 1)
/* Step 4 */   x = ++y * 2 / (1 + 1);       // (y still has value 1)
/* Step 5 */   x = ++y * 2 / 2;             // (y still has value 1)
/* Step 6 */   x = 2 * 2 / 2:               // (y now has value 2)
/* Step 7 */   x = 4 / 2;
/* Step 8 */   x = 2;
Run Code Online (Sandbox Code Playgroud)

再次,运算符的优先级得到保持。

我该如何改进这个解释?

438*_*427 9

我更喜欢使用函数调用的解释。函数调用非常明显地表明“在应用运算符之前需要评估某些内容”。

基本示例:

int x = a() + b() * c();
Run Code Online (Sandbox Code Playgroud)

必须计算为

temp = result_of_b_func_call * result_of_c_func_call
x = result_of_a_func_call + temp
Run Code Online (Sandbox Code Playgroud)

由于乘法的优先级高于加法。

然而,这3个函数调用的求值顺序是未指定的,即可以按任何顺序调用函数。喜欢

a(), b(), c()
or
a(), c(), b()
or
b(), a(), c()
or
b(), c(), a()
or
c(), a(), b()
or
c(), b(), a()
Run Code Online (Sandbox Code Playgroud)

另一个基本示例是解释运算符结合性 - 例如:

int x = a() + b() + c();
Run Code Online (Sandbox Code Playgroud)

必须计算为

temp = result_of_a_func_call + result_of_b_func_call
x = temp + result_of_c_func_call
Run Code Online (Sandbox Code Playgroud)

由于加法的从左到右结合性。但这 3 个函数调用的顺序又是未知的。

如果函数调用不是一个选项,我更喜欢类似的东西

x = a * b + c / d
Run Code Online (Sandbox Code Playgroud)

这里很明显有两个子表达式, iea * bc / d。由于运算符优先级,这两个子表达式必须在加法之前求值,但求值的顺序未指定,即我们无法判断是先进行乘法还是先进行除法。

所以可以是

temp1 = a * b
temp2 = c / d
x = temp1 + temp2
Run Code Online (Sandbox Code Playgroud)

或者它可以是

temp2 = c / d
temp1 = a * b
x = temp1 + temp2
Run Code Online (Sandbox Code Playgroud)

我们所知道的是添加必须是最后的。