冰哥哥最近在刷笔试题,昨晚问了一个没有意义,但很有意思的一个问题:
求输出:
#include<iosteam> int main(){ int a=10; a+=a-=a++; std::cout<<a<<std::endl; return 0; }
C++中,只有赋值运算符和单目运算符是右结合性,其他大部分的运算符都是左结合。
后缀运算符的优先级很高,仅次于"::“作用域解析符(与->操作符属于同一优先级,所以不能写出a->b++这样的表达式)
于是,上面的运算表达式可以写成:a+=a-=(a++);进一步,a+=a-=(10) a=a+1; a=a+1在某一时刻完成。
a++会改变自身的值,进一步影响程序中其他调用a的部分。在C++中,对变量值会进行改变的表达式被称为具有修改环境的属性。所有带有修改环境属性的程序语句(包括运算表达式、函数调用等),它们修改环境的影响将在下一个顺序点被执行完毕(即语言保证这些影响:寄存器的值、内存的值、cache的值等等能够在下一次被读取前的某时刻被完成)。
所以,要明白a+=a-=(a++)最重要的就是明确a++对其他运算符的对象:a产生的影响在什么时候完成。
C/C++语言定义(语言的参考手册)明确定义了顺序点的概念。顺序点位于:
1. 每个完整表达式结束时。完整表达式包括变量初始化表达式,表达式语句,return语句的表达式,以及条件、循环和switch语句的控制表达式(for头部有三个控制表达式);
2. 运算符 &&、||、?: 和逗号运算符的第一个运算对象计算之后;
3. 函数调用中对所有实际参数和函数名表达式(需要调用的函数也可能通过表达式描述)的求值完成之后(进入函数体之前)。
注意前面的一句话,影响的完成是在下一个顺序点之前。a+=a-=()不会触发顺序点,也就是a++对a的影响是在什么时候完成的,我们不得而知。
——————————————————————————————————————————————————————————————————————————————————————
讨论了一圈回来,我们得到的答案是此式没有答案。是啊,在真正的编写代码,写出这样的代码完全是找虐和找骂,没有正确性的保障,也没有可读取性和效率可言。
最后在结束时,再提一个关于C++运算符的误区:
a++ + b++
a++和b++谁先做?不是从左到右,在C++中没有规定大多数二元运算的两个对象的计算顺序(除了&&、|| 和 ,),也没有规定函数参数和被调函数的计算顺序。
在我们日常的程序设计中,一定不能出现依赖于修改值的先后顺序的语句,因为C/C++ 语言的规定告诉我们,任何依赖于特定计算顺序、依赖于在顺序点之间实现修改效果的表达式,其结果都没有保证。