响水凹

欢迎来到 Guang-Wen Duan (Dennis Duan) 的个人 Wiki

用户工具

站点工具


computer:c:tips:sequence_point

序列点(Sequence point)

序列点(Sequence point)是一个时间点,在此之前计算的所有副作用(side effects)已经结束,而之后计算的副作用还没有开始。

具体的说,序列点发生在如下场合:

  1. 完整表达式的尾部。完整表达式包括:
    • 变量初始化(initializer);
    • 表达式语句中的表达式;
    • 分支语句(if、switch)的控制表达式;
    • while和do循环语句的控制表达式;
    • for循环语句的每个表达式;
    • return语句的返回值表达式。
  2. 逻辑与(&&)、逻辑或(||)、三元条件符(?:)、逗号操作符(,)的第一个操作数之后。
  3. 函数调用时,所有参数求值之后。
  4. 一个完整声明(full declarator)的尾部。
  5. 库函数即将(immediately)返回之前。
  6. 格式化输入/输出函数(如printf/scanf)的转换符(conversion specifier)执行之后。
  7. 传递给bsearch/qsort等库函数的比较函数每次即将调用之前和刚刚调用之后,以及比较函数调用和该函数涉及的对象移动之间。

C标准规定:在上一个序列点和下一个序列点之间,一个对象的值至多只能被修改一次;而且,前一个值只能用于决定将要保存的值(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 read only to determine the value to be stored.)。否则结果是未定义的(undefined)。

根据标准,如下代码是未定义的:

i = ++i + 1;
a[i] = i++;

而下面的代码是确定的:

i = i + 1;

原因分析如下:

  • 对于i = ++i + 1,上一个序列点是上一条语句的结束处,而下一个序列点是结束的分号,期间i被修改了两次,所以结果是未定义的。
  • 标准的第二句话的意思是:如果在连续的两个序列点之间,某个对象的值要被修改,则对该对象的值的访问(读取)只能用来计算将要写入该对象的值。在a[i] = i++中,i既用来修改自身(i++),又用来确定a中的元素位置(a[i]),故结果未定义。
  • 在i = i + 1中,i也出现了两次,有读也有写,但这里i的读取只用来修改i自身,所以结果是确定的。

如果使用的GNU C(gcc)编译器,可利用其编译开关-Wsequence-point(包含在-Wall里),当发生序列点的未定义行为时,编译器会输出告警信息。

computer/c/tips/sequence_point.txt · 最后更改: 2014/11/01 02:02 由 127.0.0.1