有关运算符优先级的规则稍微有点复杂。在大多数情况下,这些规则确实是你所需要的,然而,有人也指出其中的一些规则本来是可以设计得更好的。
让我们快速地回顾一些有关内容:“运算符优先级”是这样一些规则的集合——这些规则规定了“运算符”(例如+,-,等等)的优先性,即哪一种运算符先参加运算。在数学中,表达式“2×3+4×5”和“(2×3)+(4×5)”是等价的,因为乘法运算在加法运算之前进行,也就是说乘法的优先级比加法高。
在c中,有16级以上的运算符优先级。尽管这么多的规则有时使c程序不易阅读,但也使C程序写起来容易多了。虽然这不是唯一的一种折衷方法,但这就是C所采用的方法。表16.1总结了运算符的优先级。
表16.1 运算符优先级总结(从高到低)
----------------------------------------------------------------------------------
优先级 运算符
----------------------------------------------------------------------------------
1 x[y](下标)
x(y)(函数调用)
x.y(访问成员)
x->y(访问成员指针)
x++(后缀自增)
x--(后缀自减)--
2 ++x(自增)
--x(自减)
&x(取地址)
*x(指针引用)
+x(同x,和数学中相同)
-x(数学求负)
!x(逻辑非)
~x(按位求反)
sizeof x和sizeof(x_t)(字节数大小)
3 (x_t)y(强制类型转换)
4 x*y(乘法)
x/y(除法)
x%y(求余)
5 x+y(加法)
x-y(减法)
6 x<<y(按位左移)
x>>y(按位右移)
7 x<y,x>y,x<=y,x>=y(关系比较)
8 x==y,x!=y(相等比较)
9 x&y(按位与)
10 x^y(按位异或) .
11 x | y(按位或)
12 x&&y(逻辑与)
13 x||y(逻辑或)
14 x?y:z(条件)
x=y,x*=y,x/=y,x+=y,x-=y,<<=,>>=,&=,^=,|=(赋值,右结合性)
16 x,y(逗号)
--------------------------------------------------------------------------------------
优先级最高的是后缀表达式,即运算符跟在一个表达式后面;其次是前缀或单目表达式,即运算符位于一个表达式的前面;再次是强制类型转换表达式。
注意:关于运算符优先级,最重要的是知道*p++和*(p++)是等价的。也就是说,在*p++中,++运算符作用在指针上,而不是作用在指针所指向的对象上。象“*p++=*q++;这样的代码在C中是随处可见的,其中的优先级和“(*(p++))=(*(q++))”中的是相同的。这个表达式的含义是“q+1,但仍用q原来的值找到q所指向的对象;p加1,但仍用p原来的值;把q所指向的对象赋给p所指向的对象”,整个表达式的值就是原来q所指向的对象。在C中你会经常看到这样的代码,并且你会有许多机会去写这样的代码。对于其它运算符,如果你记不住其优先级,可以查阅有关资料,但是,一个好的c程序员应该连想都不用想就能明白*p++的含义。
最初的C编译程序是为这样一种计算机编写的——它的某些指令对象*p++和*p++=*q++这样的代码的处理效率高得令人难以置信,因此,很多C代码就写成这种形式了。进一步地,因为象这样的C代码实在太多了,所以新机型的设计者会保证提供能非常高效地处理这些C代码的指令。
再下一级的优先级是乘法、除法和求余(也叫取模),再往后是加法和减法。与数学中的表达式相同,“2*3+4*5”和“(2*3)+(4*5)”是等价的。
再下一级是移位运算。
再往后两级分别是关系比较(例如x<y)和相等比较(x==y和x!=y)。
再往后三级分别是按位与、按位异或和按位或。
注意:关于运算符优先级,再次重要(即在知道*p++和x=y=z的含义之后)的是要知道x&y==z和(x&y)==z是不一样的。因为按位操作的运算符的优先级低于比较运算符,所以x&y==z和x&(y==z)是等价的。这两个表达式的含义都是“先看y和z是否相等(相等为1,不等为0),然后让比较结果和x进行按位与运算”,这与“先让x和y进行按位与运算,再比较其结果是否等于z”相差甚远。有人可能会争辩,按位与运算符的优先级应该高于比较运算符,但为时已晚,因为相应的标准是早在二十年前被定义的。如果你想把按位与的结果与别的东西进行比较,你就需要使用括号。
再往后两级是逻辑运算符,例如x&&y和x||y。注意,逻辑与(AND)运算符的优先级高于逻辑或(OR)运算符,这与人们讲话的方式是一致的。例如,请看下面的代码:
if(have_ticket&&have_reservation
||have_money && standby_ok){
goto_airport();
}
这段代码的含义可以这样来描述:“如果你有机票并且预定了航班,或者你有钱并且可以买到备用票,那么你就可以出发去机场了。”如果你用括号改变优先级,你就会得到一种截然不同的条件:
/* not a recommended algorithm!*/
if(have_ticket
&&(have_reservation || have_money)
&&standby_ok){
goto airport ();
}
这段代码的含义可以这样来描述:“如果你有机票,并且你预定好了航班或者有钱,并且可以买到备用票,那么你就可以出发去机场了。”
再下一级是条件表达式,例如x?y:z。这是一个if-then-else结构的表达式,而不是一条语句。条件表达式有时可以使程序简洁,有时也会造成语意的模糊。条件表达式具有右结合性,也就是说
a?b:c?d:e
等价于
a?b:(c?d:e)
这一点与else—if结构很相似。
再下一级是赋值运算。所有的赋值运算符都具有相同的优先级。与C的其它双目运算符不同,赋值运算具有“右结合性”,即它是从右向左进行的,而不是从左向右进行的。x+y+z等价于(x+y)+z,x*y+z等价于(x*y)+z,而x=y=z等价于x=(y=z)。
注意:关于运算符优先级,次重要(即在知道*p++的含义之后)的是要知道x=y=z的含义。因为赋值运算具有右结合性,所以这个表达式等价于x=(y=z),其含义是“将z的值赋给y,然后再将该值赋给x”。象a=b=c=d=O;
这样的代码是很常见的,按从右向左的顺序,它把。赋给d,再赋给c,再赋给b,最后赋给a。
c中优先级最低的是逗号运算符。它连接两个表达式,先计算第一个表达式的值,扔掉后,再计算第二个表达式的值。只有当第一个表达式具有副作用时,例如赋值或函数调用,使用逗号运算符才有意义。逗号和赋值运算符经常在for循环语句中搭配使用:
for(i=0,count=O;i<MAX;++i){
if(interestmg(a[i])){
++count:
}
}