作业和序列点:这是多么模糊?

考虑C代码a = a = a 。 没有用于赋值的序列点,因此在编译关于a的未定义操作时,此代码会产生警告。

这里有什么可能的价值? 似乎无法改变价值观。 这里有没有未定义的行为或编译器只是懒惰?

序列点违规的未定义行为规则不会在“值无法更改”的情况下成为例外。 没有人关心价值是否会发生变化。 重要的是,当您对变量进行任何类型的访问时,您正在修改该变量。 即使您为变量分配了它已经存在的值,您仍然在执行该变量的修改。 如果多个修改未被序列点分隔,则行为未定义。

人们可能会争辩说,这种“不修改的修改”不应该引起任何问题。 但语言规范并不涉及这些细节。 再次,在语言术语中,每次将某些内容写入变量时,都会对其进行修改。

此外,您在问题中使用“含糊不清”一词的事实似乎暗示您认为行为未指明 。 即“在变量的结果值是(或不是)模糊的”。 但是,在序列点违规中,语言规范并不限制自己声明结果未指定 。 它更进一步,并声明行为未定义 。 这意味着这些规则背后的基本原理不仅考虑了某个变量的不可预测的最终值。 例如,在某些虚构的硬件平台上,非顺序修改可能导致编译器生成无效代码,或类似的东西。

这实际上是未定义的行为。 a可以有任何价值。 “我想不出任何可以打破的方式”与“它保证工作”不一样。

它实际上是整个程序在执行该语句后具有“未定义的行为”。 这不仅仅是关于a的价值 – 程序可以做任何事情 ,包括进入无限循环,打印垃圾输出或崩溃。

“未定义的行为”实际上只是意味着C标准不再对程序的作用施加任何限制。 这并不能阻止你推断特定编译器在看到代码时的行为,但它仍然不是一个有效的C程序,而这正是编译器警告你的。

 int a = 42; a = a = a; 

是未定义的行为。

编写序列点规则是为了简化编译器制造商的工作。

C标准没有规则说“如果行为不明确,那么行为是未定义的。”C 1999中的实际规则说“在上一个和下一个序列点之间,一个对象应该修改其存储值最多一次通过表达式的评价。 此外,先前的值应该是只读的,以确定要存储的值。“

您的代码违反了此规则:它修改了a的值。 (3.1 3中的注释表示“修改”包括存储的新值与前一个值相同的情况。)

就是这样。 是否可以为此代码找出明确的解释并不重要。 它只会违反规则。 因为它违反了规则,所以行为未定义。

在C 2011中,该规则以更技术性的方式陈述。 6.5 2说“如果对标量对象的副作用相对于对同一标量对象的不同副作用或使用相同标量对象的值进行值计算没有排序,则行为未定义。 如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是未定义的。“当赋值运算符在对象中存储值时,这实际上是副作用 。 (主要效果是它评估存储的值。)因此,C 2011中的这个规则与C 1999规则大致相同:您可能不会对同一个对象产生两个副作用。

你很可能最终得到了理想的行为。 当有人写a=a=a他可能希望a不变,当他写a=a=b他可能希望在语句结束时将b改为b

然而,有可靠的硬件和软件组合确实打破了这一假设。 例如,考虑具有显式并行指令流的硬件。 然后可以将双重赋值编译为试图在同一寄存器中同时存储数据的两条指令。 此外,硬件设计人员也可以做到这样的假设:不允许执行该操作的指令对,并且可以对这些情况使用无关值(并简化HW)。

然后你实际上可能会遇到a=a=a实际上改变a的值而a=a=b最终会在不等于b