在两个序列点之间多次修改任何对象

int i = 42;
i = i++; /* Assignment changes variable, post-increment as well */
int a = i++ + i--;

像这样的代码经常导致对 i结果价值的猜测。但是,C 标准并未指定结果,而是指定评估此类表达式会产生未定义的行为。在 C2011 之前,该标准根据所谓的序列点形式化了这些规则 :

在前一个和下一个序列点之间,标量对象应通过表达式的计算至多修改其存储值一次。此外,先前的值应该是只读的,以确定要存储的值。

(C99 标准,第 6.5 节,第 2 段)

该方案被证明有点过于粗糙,导致某些表达方式表现出与 C99 相关的不确定行为,这似乎是不应该做的。C2011 保留了序列点,但基于测序和它称之为“之前测序” 的关系,在该区域引入了一种更细微的方法 :

如果对标量对象的副作用相对于对同一标量对象的不同副作用或使用相同标量对象的值进行的值计算未进行排序,则行为未定义。如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的。

(C2011 标准,第 6.5 节,第 2 段)

在之前排序关系的完整细节在这里描述的时间太长,但它们补充了序列点而不是取代它们,因此它们具有定义某些评估行为的效果,这些评估的行为以前是未定义的。特别是,如果在两次评估之间存在序列点,那么序列点之前的序列点将在之后的序列点排序

以下示例具有明确定义的行为:

int i = 42;
i = (i++, i+42); /* The comma-operator creates a sequence point */

以下示例具有未定义的行为:

int i = 42;
printf("%d %d\n", i++, i++); /* commas as separator of function arguments are not comma-operators */

与任何形式的未定义行为一样,观察违反排序规则的评估表达式的实际行为并不提供信息,除非是追溯意义。语言标准没有为期望这样的观察预测甚至同一程序的未来行为提供依据。