转自:http://mockbird.gotoip55.com/2011/11/30/c-中的时序点和副作用边际效应/
本文基本是以下几篇文章的摘抄和归纳。
http://en.wikipedia.org/wiki/Sequence_point
http://learn.akae.cn/media/ch16s03.html
请问 以下计算结果是什么:
int a=0;
a = (++a)+(++a)+(++a)+(++a);
这种题可能很多人面试的时候都做过,但严重怀疑出题人是不是真的懂得答案是什么,因为答案其实是未知的,想知道为什么,就必须理解清楚什么是C/C++中的顺序点和副作用。
顺序点(Sequence Point)和副作用(Side effects)分别是什么,会带来什么问题,可能很多初学者对此并不了解,很多老手也可能会在这个问题上载跟头。
标准定义
ISO14882:1998中1.9节第7款的叙述:顺序点是前一求值中所有副作用已经结束而下一求值中任何副作用尚未开始的地方。具体来说,有以下几个地方:
1.在每一个完整表达式结束的地方是Sequence Point。用ISO14882中的术语,就是完全表达式(full-expression)结束的地方。比如有f(); g();这样两条语句,f()和g()是两个完整的表达式,f()的Side Effect必定在g()之前发生。
2.在一个函数所有参数求值完以后并且在准备调用该函数之前是Sequence Point。比如调用foo(f(), g())时,foo、f()、g()这三个表达式哪个先求值哪个后求值是Unspecified,但是必须都求值完了才能做最后的函数调用,所以f()和g()的Side Effect按什么顺序发生不一定,但必定在这些Side Effect全部作用完之后才开始调用foo函数。
3.当一个函数返回的时候,就是在返回值已经被复制以后,而在运行该函数所有外部之前是Sequence Point。
4.在库函数即将返回时是Sequence Point。这条规则似乎可以包含在上一条规则里面,因为函数返回时必然会结束掉一个完整的表达式。而事实上很多库函数是以宏定义的形式实现的,并不是真正的函数,所以才需要有这条规则。
5.在下列表达式中的第一个表达式求值之后是Sequence Point。假设这里用的都是内置运算符而没有被重载。
- expr1 && expr2
- expr1 || expr2
- expr1 ? expr2:expr3
- expr1 , expr2
6.在一个完整的声明末尾是Sequence Point。所谓完整的声明是指这个声明不是另外一个声明的一部分。比如声明int a[10], b[20];,在a[10]末尾是Sequence Point,在b[20]末尾也是。
C++里说,如果在两个时序点之间有两个或多个副作用,那么这些副作用的时序是不定的。如果表达式的值依赖于这些副作用间的顺序,那么表达式的值也是不定的。