顺序点,C语言冰山位于海面下的角落。
副作用side effect,简单理解就是变量值被改变这个事实。
i++这个表达式,副作用就是i被增加1。
a = 10;这个表达式,副作用就是a被赋值成10。
一个复杂的表达式,其求值过程是有迹可循的。
例如:i++ + ++i 这个表达式,根据运算符的结合性或优先级关系,可以被人为是如下等效形式:(i++) + (++i).现在的问题来了,它的求值结果是什么?答案是:未定义行为(undefined)。
未定义的意思是:无法预测结果。
对比另一个叫做为未指定行为(unspecified) ,说的是有几个结果,发生哪一个不知道。
显然,未定义是更危险的。
这里面涉及到顺序点这个事。
具体细节参考sequence point « Eternity
简单的说,就是在顺序点之前,何时完成副作用,是不确定的事,不能根据子表达式,就说在此子表达式完成副作用。
顺序点是语法解析过程,对指定的几个词法位置的特殊标记。
就好比道路上的路牌作用。
常用的顺序点(我用@符号表示这个路牌):
1) 分号位置。
a = 10 ;@
2) 逗号表达式位置
a = 10 ,@ i++ ,@ a=i ;@
3) 三目运算符
a>0 ?@ i++ :@ j++ ;@
4) ......
副作用被限制在顺序点之间的任意位置发生。
即两个@路牌之间发生。
回到 (i++) + (++i)这个面试和考试常考的地方,因为+号不是顺序点,所以,不能断言出i++的副作用在+之前完成。
可以想象成i++和++i的副作用是多线程方式(parallel)方式进行的。
这是个多线程写。
另一个例子是a[i] = i++;因为[]和=都不是合规的顺序点,因此将发生多线程的读写。
因此上述两个例子的结果都是不确定的。
实际上没有多线程,只是想象,帮助你理解为什么不能那么写程序,也不能拿这种例子考试别人。
只是编译器厂家可以做生成的指令重排列做效率优化(只要保证在顺序点之前实现副作用)。
重排列指令顺序后,等效于多线程(单核)指令交替执行的效果。
下面是各个编译器上机实验:
#include
int main()
{
int i = 1;
int a = i++ + ++i;
std::cout << a << std::endl;
return 0;
}
GCC: 提示 undefined behavior。
不是说未定义变量i。
CLANG:提示的更清楚,多个写 *** 作发生了。
VS2005:未提示
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)