35

命令式编程中的顺序点定义了计算机程序执行中的任何点,在该点处,可以保证先前评估的所有副作用都已执行,并且尚未执行后续评估的副作用。

这是什么意思?有人可以用简单的话解释一下吗?

4

4 回答 4

54

当一个序列点出现时,基本上意味着你保证之前的所有操作都完成了。

在没有中间序列点的情况下更改变量两次是未定义行为的一个示例。

例如,i = i++;未定义,因为对 . 的两次更改之间没有序列点i

请注意,不仅仅是两次更改变量会导致问题。这实际上是涉及任何其他用途的更改。在讨论事物如何排序时,该标准使用术语“价值计算副作用”。例如,在表达式a = i + i++中,i(值计算)和i++(副作用)可以以任意顺序进行。

维基百科有一个C 和 C++ 标准中的序列点列表,尽管最终列表应始终取自 ISO 标准。来自 C11 附录 C(释义):


以下是标准中描述的序列点:

  • 在函数调用和实际调用中函数指示符和实际参数的评估之间;
  • &&在运算符、||,;的第一个和第二个操作数的计算之间
  • 在条件?:运算符的第一个操作数的计算与第二个和第三个操作数中的任何一个计算之间;
  • 完整声明符的结束;
  • 在完整表达式的评估和要评估的下一个完整表达式之间。以下是完整的表达式:
    • 初始化器;
    • 表达式语句中的表达式;
    • 选择语句(ifswitch)的控制表达式;
    • whileor do 语句的控制表达式;
    • 语句的每个表达式for
    • return 语句中的表达式。
  • 紧接在库函数返回之前;
  • 在与每个格式化输入/输出函数转换说明符关联的动作之后;
  • 在每次调用比较函数之前和之后,以及在对比较函数的任何调用与作为参数传递给该调用的对象的任何移动之间。
于 2010-08-26T13:13:49.477 回答
13

关于序列点需要注意的重要一点是它们不是全局的,而是应该被视为一组局部约束。例如,在声明中

a = f1(x++) + f2(y++);

在 x++ 的计算和对 f1 的调用之间有一个序列点,在 y++ 的计算和对 f2 的调用之间有另一个序列点。但是,不能保证 x 是否会在调用 f2 之前或之后递增,也不保证 y 是否会在调用 x 之前或之后递增。如果 f1 更改 y 或 f2 更改 x,则结果将是未定义的(编译器生成的代码可以合法地读取 x 和 y、递增 x、调用 f1、检查 y 与先前读取的值以及--如果它发生了变化——疯狂地寻找并销毁所有 Barney 视频和商品;我不认为任何真正的编译器生成的代码会真正做到这一点,唉,但在标准下它是允许的)。

于 2010-08-26T15:52:57.210 回答
5

用一个例子扩展paxdiablo的答案。

假设声明

x = i++ * ++j;

有三个副作用:将结果赋值i * (j+1)给 x,将 1 加到 i,以及将 1 加到 j。应用副作用的顺序未指定;i 和 j 可以在被评估后立即递增,或者在两者都被评估之后但在分配 x 之前它们可能不会递增,或者在分配 x 之后它们可能不会递增。

序列点是应用了所有副作用的点(x、i 和 j 都已更新),无论它们应用的顺序如何。

于 2010-08-26T15:10:05.000 回答
4

这意味着编译器可能会进行时髦的优化、技巧和魔术,但必须在这些所谓的序列点处达到明确定义的状态。

于 2010-08-26T13:18:42.800 回答