C语言中,优先级规定了当多个运算符同时存在时表达式的求值顺序;结合性指出当多个优先级相同的运算符存在时表达式的求值顺序,可以消除歧义,优先级相同的运算符它们的结合性也一致。
C语言的优先级列表如下(最新版取自:http://en.cppreference.com):
例如:
1、优先级示例
num_1 = test << 3 + num_2 ;
因为算术运算符 ‘+’ 的优先级(4)要高于逻辑运算符 ‘<<’(5),所以该语句实际相当于:num_1 = test << (3 + num_2),这并非本意。为了清楚的表达,就需要人为干预一下,写成:
num_1 = (test << 3) + num_2 ;
因为括号的优先级最高,则先计算括号里边的表达式,然后再计算括号外边,这跟常见的数学运算是一致的。
所以,在处理运算符优先级不是很明了的情况时,一定要人为干预增加括号,这样可以完全避免歧义发生;虽然添加括号可能会使表达式难以理解,但是为了正确性只能如此,不过可以利用空格对括号进行区域划分,最大限度增加可读性。例如:
if ( ( 2 < num_1) && (num_1 < 5) )
2、结合性示例
num = a / b * c ;
因为 ‘/’ 和 ‘*’ 的优先级相同,结合性就决定表达式的求值顺序,该算术表达式的结合性是自左向右,则从左边开始开始运算,实际解释成:
num = ( a / b ) * c ;
优先级和结合性分解:
1、优先级最高的是数组下标、函数调用操作符、结构体成员选择符、后自增/自减等,都是自左向右的结合性。因此
2、其次是单目运算符,包括指针解引用、取地址符、前自增/自减等,自右向左的结合性。例如: *P++ 应该解释成 *(P++) ,即取P指向的对象,然后P自增1,虽然后自增++优先级高于*,但是后自增的特性决定了先对P进行运算,运算完成后再P自增1;(*P)++指先取P所指向的对象,然后对该对象加1,由于括号优先级高则先计算括号内部表达式,++运算符对括号这个数据整体起作用。
3、接着是双目运算符,其中算术运算符的优先级最高,移位运算符次之,关系运算符再次之,然后是逻辑运算符。
4、剩下的是三目运算符和赋值运算符。
5、任何一个逻辑运算符(8、9、10、11、12)的优先级低于任何一个关系运算符(6、7)。
6、移位运算符(5)的优先级比算术运算符(3、4)低,比关系运算符(6、7)高。
7、优先级规则如此复杂,想全部记住并非易事,所以有专家建议要在操作符上添加括号,显式地规定优先级减小理解难度,同时也避免二义性产生。
8、在代码书写时,尽量不要因为结合性带来歧义,尽量拆开表达式或者添加括号。
9、虽然运算符的优先级和结合性都已经明确定义,但是大部分表达式里各个操作数的计算顺序是不确定的(&&和||是定义好的,从左到右的顺序),例如 a * d + b * c 中,两个*运算到底先进行哪一个是未定义的,这样可以让编译器设计人员根据处理机结构选择合适的方法产生效率最高的代码。因为未定义,所以我们的代码运算结果一定不能依赖计算顺序。
易错项分析
1、*P.num :“.” 的优先级(1)高于 “*” 的优先级(2),故含义是:*(P.num) 对P取num的偏移,然后对num进行解引用操作;易错:(*P).num 对P进行解引用,然后取该对象的num字段。
2、int *array [ ] :"[ ]" 的优先级(1)高于 “*” 的优先级(2),故含义是:int * (array [ ]) array是个数组,数据由int指针组成;易错:int (*array) [ ] array是个指向int数组的指针。
3、int * func( ) :“( )” 的优先级(1)高于 “*” 的优先级(2),故含义是:int *(func( )) func是个返回int指针的函数;易错:int (*func) ( ) func是个函数指针,其指向的函数返回值为int类型。
4、( num & mask != 0 ) :“!=” 的优先级(7)高于 “&” 的优先级(8),故含义是:( num & (mask != 0) ); 易错:( (num & mask) != 0 ) 。
5、( result = func() != 0 ) :因为 “!=” 的优先级(7)高于 "=" 的优先级(14),故含义是:( result = (func() != 0) ) ;易错:( (result = func()) != 0 ) 。
6、num << 5 + result :因为算数运算符的优先级高于移位运算符,故含义是:num << (5 + result) ;易错:(num << 5) + result。
7、num = 3 , 5 :虽然逗号运算符的值是最右边操作数的值,但是逗号运算符的优先级最低,这里 “=” 的优先级较高,故含义是:(num = 3) , 5;易错: num = ( 3, 5 ) 。
关于前自增/减和后自增/减优先级示例:
int num_a = 5;
int num_b = 7;
int num_c = 0;
int * num_b_ptr = &num_b;
int _tmain(int argc, _TCHAR* argv[])
{
num_c = num_a + num_b++;
num_c = *num_b_ptr++;
num_c = ++*num_b_ptr;
return 0;
}
vs2010下反汇编结果如下:
可根据运算符优先级和汇编代码自行分析,熟练掌握相关技巧,必要时记忆。