C++ 运算符辨析——优先级与结合律

运算符的优先级

图源摘自C++ Primer第五版

image

image

如何记忆?其实大部分不用记忆,因为大部分符号是符合数学认知规律的,比如乘除>加减>逻辑>赋值>逗号(比方说数学上我们说\(a<3\wedge b<4\),你绝不可能想到先运算\(3\wedge b\)吧)。不过有一些让人容易混淆的占少数,这里列出一些我认识的:

  • 自增自减的优先级非常高!他们的优先级高于引用和解引用,所以*p++*(p++)是相当容易弄晕的。

  • 类型转换的优先级很高!C++转换的可读性很好,不必多言,不过C风格转换不那么直观,由于优先级很高,它一般只作用于最近的一个变量。

  • <<>>和优先级比较高,仅在加减之下。l + r >> 1可以求中点值。另外使用cin和cout也要注意,不要发癫了。

  • 三目运算符可以嵌套得到ifelse的效果,如a.x!=b.x ? a.x<b.x : a.y!=b.y ? a.y<b.y : a.z<b.z

优先级和类型声明推断

然而上面的表还有一部分没有被注意到,那就是第二大栏。怎么连函数调用类型构造都来了?在面对一个复杂类型的声明时,优先级规律仍然有效。

一个变量的类型可能是什么?无非是普通类型,指针,引用,数组,函数这些,他们都可以用上面的规则推断出来。

比如典中典例子:int *a[10]int (*a)[10],中括号的优先级比星号高,因此a先与[10]结合成为数组,数组的类型是*,什么*?是int*。而第二个打了括号,a先和*结合成为指针,指针指向的类型是?int[10]。

再比如函数指针和返回指针的函数:int *f(int)int (*f)(int),第一个f先和括号结合,成为函数,返回类型是int*,第二个打了括号就先和*结合为指针,指针指向的类型是一个函数?什么函数?参数int返回int的函数。

两个还可以混在一起,这个时候数组往往放在后面:比如int(& (*f)()) [10],我们从内往外按优先级结合,这是一个指针,指向一个函数,函数返回一个引用,是int[10]的引用。

结合律和求值顺序

结合律在上面表格的最左一列,给出的信息并不令人意外。一元运算符不存在结合律,大部分二元运算符常规运算的结合律为左,即a+b+c会先计算a+b,结果再执行+c。逗号也会严格从左向右执行。比较运算符也是先左后右。不过各种赋值结合律为右,这意味这可以使用a = b = c的连环赋值方式,将c赋值给b,再将结果赋值给a。

求值顺序则是另一个问题,相当于“交换律”,提到求值顺序就不得不提谭浩强书中大名鼎鼎的的i++ + i或者a[i]=i++问题,这个问题等于在问a+b先确定a还是先确定b?在标准规范的今天,我们的答案是“未定义”!也就是说先求任何一边的值都是允许的。标准没有作出规定,编译器也不会判断这些东西,实际应用中不应该出现这种情况。

实际上,绝大部分运算符都没有规定求值顺序,这些会妨碍优化。确认不会因为求值顺序出现错误是程序员的责任。唯一的例外是&&||,这两个逻辑运算符会严格按照短路规则从左到右求值,在确定结果后就不再继续运算。

posted @ 2021-09-26 15:08  Ofnoname  阅读(647)  评论(0编辑  收藏  举报