顺序点
外观
![本页使用了标题或全文手工转换](http://178.128.105.246/cars-http-upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Zh_conversion_icon_m.svg/35px-Zh_conversion_icon_m.svg.png)
顺序点,也称作序列点,是计算机程序中一些执行点,在该点处之前的求值的所有的副作用已经发生,在它之后的求值的所有副作用仍未开始。在C与C++程序设计语言中,表达式的值依赖于它的子表达式的求值顺序。增加更多的顺序点限制了可能的求值顺序,能保证有一个一致结果。
C++11中,顺序点概念已经被sequenced before这种方法取代:直接指出一个求值是在另一个求值之前,或者两个求值是无顺序的。[1][2]无顺序的求值可以重叠进行。
歧义例子
[编辑]考虑两个函数f()
与g()
。在C与C++中,加法运算符+
不是一个顺序点,因此表达式f() + g()
可能会先调用f()
,或先调用g()
。逗号运算符引入了一个顺序点,因此表达式f(), g()
的求值顺序是确定的:首先调用f()
,然后调用g()
。
当一个变量在一个表达式修改不止一次,顺序点也发挥作用。一个典型的C语言例子是表达式i = i++
,其中i
的结果值是有二义性的,依赖于表达式求值顺序:自增运算可能发生在赋值之前、之后或者交错进行。在C与C++语言中,这会导致未定义行为。[3]
C与C++的顺序点
[编辑]在C[4]与C++[5]中,顺序点出现在下述位置:(C++的重载操作符的行为类似于函数)
&&
(逻辑与)、||
(逻辑或)、逗号运算符的左操作数与右操作数求值之间(前两者是短路求值的一部分)。例如,表达式*p++ != 0 && *q++ != 0
,子表达式*p++ != 0
的副作用都会在试图访问q
之前完成。- 三元条件运算符的第一个操作数之后,第二或第三操作数之前。例如,表达式
a = (*p++) ? (*p++) : 0
在第一个*p++
之后存在顺序点,因而在第二个*p++
求值之前已经做完一次自增。 - 完整表达式结束处。包括表达式语句(如赋值
a = b;
),返回语句,if
、switch
、while
、do
-while
语句的控制表达式,for
语句的3个表达式。 - 函数调用时的函数入口点。函数实参的求值顺序未指定,但顺序点意味着这些实参求值的副作用在进入函数时都已经完成。表达式
f(i++) + g(j++) + h(k++)
,调用f()
,g()
,h()
的顺序未指定,i
,j
,k
的自增顺序也未指定。函数调用f(a,b,c)
的实参列表不是逗号运算符,a
,b
,c
的求值顺序未指定。 - 函数返回时,在返回值已经复制到调用上下文。(仅C++标准指出这一顺序点[6])
- 初始化的结束。例如,声明
int a = 5;
中的对5
求值之后。 - 初始化列表的以逗号分割的各个初始化值,严格遵照从左至右求值。例如:
int a[3] = {i++,j--,foo(101)};
注意,此处不是逗号运算符。(从C++11标准指出这一顺序点) - 在声明序列的每个声明(declarator)之间。例如,
int x = a++, y = a++
的两次a++
求值之间。[7]注意,此例不是逗号运算符。
参考文献
[编辑]- ^ ISO/IEC 14882:2011. [2012-07-04]. (原始内容存档于2013-05-17).
- ^ A finer-grained alternative to sequence points (revised) (WG21/N2239 J16/07-0099). [2012-07-05]. (原始内容存档于2021-03-07).
- ^ Clause 6.5#2 of the C99 specification: "Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored."
- ^ C99 规范的附录 C 中列出了可以假定序列点位置的情况。
- ^ The 1998 C++ standard lists sequence points for that language in section 1.9, paragraphs 16–18.
- ^ C++ standard, ISO 14882:2003, section 1.9, footnote 11.
- ^ C++ standard, ISO 14882:2003, section 8.3: "Each init-declarator in a declaration is analyzed separately as if it was in a declaration by itself."
- Question 3.8 (页面存档备份,存于互联网档案馆) of the FAQ for comp.lang.c