2.12 运算符优先级与求值次序
表 2-1 总结了所有运算符的优先级与结合性,其中的一些规则我们还没有讲述
同一行中的各运算符具有相同的优先级,各行间从上往下优先级逐行降低
例如 *
、/
、%
具有相同的优先级,它们的优先级都比二元运算符 +
、-
高
运算符 ()
表示函数调用
运算符 ->
和 .
用于访问结构成员,第 6 章将讨论这两个运算符以及 sizeof
(对象长度)运算符
第 5 章将讨论运算符 *
(通过指针间接访问)与 &
(对象地址)
第 3 章将讨论逗号运算符
运算符 | 结合性 |
---|---|
() [] -> . | 从左至右 |
! ~ ++ – + - * (type) sizeof | 从右至左 |
* / % | 从左至右 |
+ - | 从左至右 |
<< >> | 从左至右 |
< <= > >= | 从左至右 |
== != | 从左至右 |
& | 从左至右 |
^ | 从左至右 |
| | 从左至右 |
&& | 从左至右 |
|| | 从左至右 |
?: | 从右至左 |
= += -= *= /= %= &= ^= |= <<= >>= | 从右至左 |
, | 从左至右 |
表 2-1 | |
注:一元运算符 + 、- 、& 、* 比相应的二元运算符 + 、- 、& 、* 的优先级高 |
注意,位运算符 &
、^
、|
的优先级比运算符 ==
与 !=
的低
这意味着,位测试表达式,如 if ((x & MASK) == 0) ...
必须用圆括号括起来才能得到正确结果
同大多数语言一样,C 语言没有指定同一运算符中多个操作数的计算顺序(&&
、||
、?:
、,
运算符除外)
例如,在形如 x = f() + g();
的语句中,f()
可以在 g()
之前计算,也可以在 g()
之后计算
因此,如果函数 f
或 g
改变了另一个函数所使用的变量,那么 x
的结果可能会依赖于这两个函数的计算顺序
为了保证特定的计算顺序,可以把中间结果保存在临时变量中
类似地,C 语言也没有指定函数各参数的求值顺序,因此,下列语句
printf("%d %d\n", ++n, power(2, n)); /* 错 */
在不同的编译器中可能会产生不同的结果,这取决于 n
的自增运算在 power
调用之前还是之后执行
解决的办法是把该语句改写成下列形式:
++n;
printf("%d %d\n", n, power(2, n));
函数调用、嵌套赋值语句、自增与自减运算符都有可能产生 “ 副作用 ”
在对表达式求值的同时,修改了某些变量的值
在有副作用影响的表达式中,其执行结果同表达式中的变量被修改的顺序之间存在着微妙的依赖关系
下列语句就是一个典型的令人不愉快的情况:
a[i] = i++;
问题是:数组下标 i
是引用旧值还是引用新值?
对这种情况编译器的解释可能不同,并因此产生不同的结果
C 语言标准对大多数这类问题有意未作具体规定
表达式何时会产生这种副作用(对变量赋值),将由编译器决定的,因为最佳的求值顺序同机器结构有很大关系
ANSI C 标准明确规定了所有对参数的副作用都必须在函数调用之前生效,但这对前面介绍的 printf
函数调用没有什么帮助
在任何一种编程语言中,如果代码的执行结果与求值顺序相关,则都是不好的程序设计风格
很自然,有必要了解哪些问题需要避免,但是,如果不知道这些问题在各种机器上是如何解决的,就最好不要尝试运用某种特殊的实现方式