一个朋友问我用简单的方式解释运算符优先级和运算顺序的区别。这是我向他们解释的内容:
我们来看一个例子 -
int x;
int a = 2;
int b = 5;
int c = 6;
int d = 4;
x = a * b / (c + d);
在这里,
x
的最终值将变成1
。这是因为首先会将c
和d
的值相加(6+4
),然后将a
和b
的值相乘(2*5
),最后进行除法运算(10/10
),结果导致最终值变为1
,并分配给x
。所有这些都由运算符优先级指定。在此示例中,括号强制先执行加法,而不是乘法和除法,即使加法的优先级较低。另外,乘法先于除法执行,因为乘法和除法具有相同的优先级,并且它们都具有从左到右的结合性。
现在是重要的部分,即该表达式的评估顺序。
在某个系统上,表达式的计算顺序可能如下 -
/* 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;
请注意,在任何一步中,始终确保操作符的优先级得以保持,即使在第二步中将
b
替换为5
,乘法也不会在第七步之前进行。因此,尽管不同系统的计算顺序不同,但操作符的优先级始终得以保持。在另一个系统上,求值的顺序可能如下:
/* 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;
再次强调,运算符优先级得到保留。
在上面的例子中,整个行为是明确定义的。其中一个原因是所有变量都是不同的。
从技术角度来看,在此示例中行为是明确定义的原因是没有对任何变量进行无序修改。
因此,在任何系统上,x
最终总是被赋值为 1
。
现在,让我们将上面的例子改成这样:
int x;
int y = 1;
x = ++y * y-- / (y + y++);
这里,最后被分配给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;
再次强调,运算符优先级得到维护。
在其他系统上,求值顺序可能是这样的:
/* 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;
再次强调,运算符优先级得以维持。
如何改进这个解释?
&&
等。 - Lundin