笔试实例:"序列点" 是什么

序列点是一个时间点(在整个表达式全部计算完毕之后或在||、&&、? : 或逗号运算符处, 或在函数调用之前), 此刻尘埃落定, 所有的副作用都已确保结束。ANSI/ISO C 标准这样描述:在上一个和下一个序列点之间, 一个对象所保存的值至多只能被表

笔试实例:"序列点" 是什么

达式的计算修改一次。而且前一个值只能用于决定将要保存的值。第二句话比较费解。它说在一个表达式中如果某个对象需要写入, 则在同一表达式中对该对象的访问应该只局限于直接用于计算将要写入的值。这条规则有效地限制了只有能确保在修改之前才访问变量的表达式为合法。例如i = i+1 合法, 而a[i] = i++ 则非法

拓展:

int i = 3;

i = i++;

cout << i;

结果是什么?有人可能会说是3,也有人可能会说是4,更多的人在骂出题的人白痴,但这语句究竟有何问题呢?未必每个人都清楚。

有些人也许马上会说,这是“未定义行为”。没错,这是一个典型的未定义行为。i = i++这个表达式合乎C++语法,能够顺利编译通过,但是执行的结果,标准说“未定义”。为什么是“未定义”,深究起来,要从序列点说起。

序列点是程序中这样的一些点:通俗地说,执行至此,之前的语句都已经彻底执行干净执行完了,之后的语句还完全没开始执行;更常见、更严谨但略晦涩的说法是,之前的语句对现场环境的改变已经全部完成,之后的语句对现场环境的改变还没有开始。啥是现场环境呢?就是程序执行到某一点的那个状态,包括变量的内容、文件的内容等。

这跟最开始那个例子有什么关系呢?关键的问题来了:标准规定,两个序列点之间,程序执行的顺序可以是任意的。没错,正如你猜的那样,C++标准规定一个完整的表达式结束之后有一个序列点,而例子中i = i++是位于两个序列点之间的。编译器可以先算完i++,再写结果给i,也可以先将i = i,再令i++。按前面的方法算,i先自增变为4,然后i++返回3,于是i被赋值为3;按后一种方法算,i先被赋值为3,随后自增变成4。标准说了,这两种处理方法,编译器你爱选那种就选哪种,随便。如果谁写的程序像这样依赖执行的顺序,让他自己哭去!

等等,有人要问了,++的优先级难倒不是高于=吗?显然应该先执行++啊。这里有个概念的问题,前一段说的编译器先算i = i,绝不是说令=的优先级比++还高了。如果那样的话,表达式将变成 (i = i)++,也就是ator = (i). operator ++,执行++的主体变成i = i这个表达式的返回值了。上一段所说的先计算i = i,实际上还是先计算i++,只不过是先返回了i的值,然后推迟了将i自增1的操作先去干别的(i = i)去了,回头再来给i自增1。