如果一个表达式包含两个或两个以上的运算符,那么操作数的结合方式将决定这个表达式的结果。为了简便,我们把包含两个或两个以上运算符的表达式称为复合表达式。例如:3 + 2 * 5
如果 3 和 2 结合在一起,那么这个表达式的结果将会是 5 * 5 等于 25。当然,C 语言里,本例的 3 不会和 2 结合,而是 2 和 5 结合,得到 3 + 10 等于 13。
运算符的优先级(precedence)和结合律(associativity)决定操作数的结合方式。当复合表达式中的运算符的优先级不同时,操作数的结合方式由优先级决定。当复合表达式中的运算符的优先级相同时,操作数的结合方式由结合律决定。不过,我们也可以使用括号强制把操作数结合在一起。例如:
(3 + 2) * 5
强制把 3 和 2 结合在一起。
1. 优先级
操作数的结合方式决定复合表达式的值。当复合表达式中的运算符的优先级不同时,操作数的结合方式由优先级决定。操作数总是围绕优先级较高的运算符进行结合。例如:
8.0 + 20.0 / 4.0
因为除法运算符的优先级比加法运算符高,所以 20.0 和 4.0 结合,得到 5.0,然后 8.0 和 5.0 结合,得到 13.0。
2. 结合律
当复合表达式中的运算符的优先级相同时,操作数的结合方式由结合律决定。例如:
8.0 + 20.0 / 4.0 * 2.0
乘法运算符的优先级和除法运算符相同。不过,由于它们的结合率都是从左到右(即从左到右进行结合),所以 20.0 和 4.0 结合;得到的结果 5.0 再和 2.0 结合,得到 10.0;8.0 和 10.0 结合,得到 18.0。
大多数运算符的结合律都是从左到右,不过也有从右到左的(例如赋值运算符)。
3. 使用括号强制结合操作数
使用括号可以强制把操作数结合在一起。被括号括住的个体进行处理,这个个体同样要受到优先级和结合律的约束。例如:
(8.0 + 20.0 / 4.0) * 2.0
强制把 8.0 + 20.0 / 4.0 当成一个个体来处理。因为除法运算符的优先级比加法运算符高,所以 20.0 和4.0 结合,得到 5.0;然后 8.0 和 5.0 结合,得到 13.0;13.0 和 2.0 结合得到 26.0。
-9 + 4 * 5 + 6 + 8 * 7 - 1
根据优先级和结合律,这个表达式的结合方式等同于:
(((((-9)+(4 * 5))+ 6)+(8 * 7))- 1)
由于赋值运算符的结合律是从右到左,所以
i = j = k = l
的结合方式等同于:
(i =(j =(k = l)))
4. 运算顺序
优先级和结合律仅能确定操作数的结合方式,却不能决定操作数的运算顺序。例如:
5 * 3 + 8 * 4
根据优先级和结合律,我们知道,这个表达式的结合方式等同于
(5 * 3) + (8 * 4) // 5 * 3 和 8 * 4 是 + 的操作数
但是, 到底是先运算 5 * 3 还是先运算 8 * 4,我们并不能确定。它们运算的先后是由编译器决定的。标准不规定操作数的运算顺序是为了让特定系统的编译器选择对该系统来说效率最高的运算顺序。但无论它们的运算先后如何,最终得到的结果都是 47。
下面我们来看一个小程序。
/*--------------------------------------------------------------
| 作者: Antigloss @http://stdcpp.cn@ 蚂蚁的 C/C++ 标准编程
|
| 功能: 演示优先级和结合律对运算结果的影响
-------------------------------------------------------------*/
#include
int main(void)
{
int var1, var2;
var1 = var2 = -(9 + 4) * 5 + (6 + 8 * (7 - 1));
printf("var1 = var2 = %d\n", var1);
return 0;
}
请认真阅读以上程序,想想会出现什么结果,然后编译运行,看看运行结果和您想象的是否一样。
因为优先级和结合律不能决定操作数的运算顺序,所以我们不知道到底是先运算 -(9 + 4) * 5 还是先运算 (6 + 8 * (7 - 1))。它们运算的先后是由编译器决定的。假设先运算 -(9 + 4) * 5,虽然加法运算符的优先级较低,但是括号强制把 9 和 4 结合在一起,得到 13。负号运算符的优先级比乘法运算符高,其作用于 13 得 -13。于是我们得到:
var1 = var2 = -13 * 5 + (6 + 8 * (7 - 1));
由于乘法运算符的优先级比加法运算符高,所以 -13 和 5 结合在一起,得到 -65:
var1 = var2 = -65 + (6 + 8 * (7 - 1));
在 (6 + 8 * (7 - 1)) 中,括号强制把 7 和 1 结合,得 6:
var1 = var2 = -65 + (6 + 8 * 6);
因为* 优先级高于 +,于是我们得到:
var1 = var2 = -65 + (6 + 48);
进而
var1 = var2 = -65 + 54;
var1 = var2 = -11;
因为赋值运算符的结合率是从右到左,所以 -11 被赋值给 var2,接着 var2 被赋值给 var1。最终的结果是,var1 和 var2 相等,它们的值都是 -11。
5. 规定了操作数的运算顺序的运算符
C 语言中,有四个运算符对其操作数的运算顺序做了明确的规定:
&& || ?: ,
除了这四个运算符以外,其它运算符都没有规定其操作数的运算顺序。
6. C 语言提供的所有运算符
下图列出了 C 语言提供的所有运算符,按优先级从高到低进行排列,优先级相同的运算符放在同一行。
参考资料:C Primer 5th Edition
C99 标准
本文版权归蚂蚁的 C/C++ 标准编程以及 作者antigloss共同所有,转载请注明原作者和出处。谢谢。