前提
表达式有由2种功能:
- 表达式会产生一个结果
- 表达式可能产生一个“副作用”(side effect)
- 比如i++,返回值是i,产生的副作用是增加1
- 比如++i,返回值是i+1,副作用也是i增加1
表达式的副作用何时生效,以及运算对象的求值顺序,会影响表达式的值。比如:
int a=4,b;
b = a++ + a
上面的语句cppcheck会给出以下错误提示:
表达式的结果取决于副作用产生的顺序
- 如果“+”的两个对象从左往右计算
- a++的副作用在计算第二个操作数a时已经生效那么计算值是9
- 如果a++的副作用在进入到"+"运算时,才生效,那么b的值是8
- 如果"+"的两个对象从右往左计算:
- b的值都是8
因此下面讲2个概念
- 顺序点
- 求职顺序
顺序点
在C、C++等允许表达式有副作用的语言中,通常都规定了顺序点,顺序点就是表达式中的副作用的最晚生效时刻。在程序执行中,存在一系列的顺序点,一旦执行到一个顺序点,此前的副作用都必须生效。但是在顺序点之间并没有任何保证。
C/C++明确定义了顺序点的概念。顺序点位于:
- 每个完整表达式结束时。完整表达式包括变量初始化表达式,表达式语句,return语句的表达式,以及条件、循环和switch语句的控制表达式(for头部有三个控制表达式)
- 运算符 &&、||、?: 和逗号运算符的第一个运算对象计算之后
- 函数调用中对所有实际参数和函数名表达式(需要调用的函数也可能通过表达式描述)的求值完成之后(进入函数体之前)
假设时刻ti和ti+1是前后相继的两个顺序点,到了ti+1,必须实现ti之后发生的所有副作用。当然也可以不等到时刻ti+1,完全可以选择在时段 [ti, ti+1] 之间的任何时刻实现在此期间出现的副作用。
求值顺序
(a + b) * (c + d)
“*”的两个运算对象哪个先算?
fun(a++, b, a+5)
fun及其三个参数按什么顺序计算?
对第一个表达式,采用任何计算顺序都没关系,因为其中的子表达式都是引用透明的。
第二个例子里的实参表达式出现了副作用,计算顺序就非常重要了。在计算第二个表达式时,首先按照某种顺序算fun、a++、b和a+5,之后是顺序点,而后进入函数执行。
Java明确规定求值顺序是从左到右。
参考来源:
http://blog.timd.cn/evaluation-order/