考虑C代码a = a = a
。没有用于赋值的序列点,因此在编译a
上的未定义操作时,此代码会生成警告。
a
可能有什么值?似乎a
无法改变值。这里是否存在未定义的行为,或者编译器是否只是懒惰?
答案 0 :(得分:3)
序列点违规的未定义行为规则在“值无法更改”的情况下不会出现异常。没有人关心价值是否会发生变化。重要的是,当您对变量进行任何 write 访问时,您修改该变量。即使您为变量分配了它已经存在的值,您仍然在对该变量进行修改。如果多个修改没有被序列点分开,则行为是未定义的。
有人可能会争辩说,这种“不修改的修改”不应该引起任何问题。但语言规范并不涉及这些细节。再次,在语言术语中,每次将某些内容写入变量时,都要对其进行修改。
此外,您在问题中使用“含糊不清”一词的事实似乎暗示您认为行为是未指定。即如“变量的结果值是(或不是)模糊的”。但是,在序列点违规中,语言规范并不限制自己声明结果是未指定。它更进一步,并声明行为 undefined 。这意味着这些规则背后的基本原理不仅考虑了某个变量的不可预测的最终值。例如,在某些虚构的硬件平台上,非顺序修改可能会导致编译器生成无效代码,或者类似的东西。
答案 1 :(得分:2)
这实际上是未定义的行为。 a
可以有任何价值。 “我想不出任何可以打破的方式”与“它保证工作”不一样。
答案 2 :(得分:2)
实际上,整个程序在执行该语句后具有“未定义的行为”。这不仅仅是a
的价值 - 程序可以做任何,包括进入无限循环,打印垃圾输出或崩溃。
“未定义的行为”实际上只是意味着C标准不再对程序的作用施加任何限制。这并不能阻止你推断特定编译器在看到该代码时的行为方式,但它仍然不是一个有效的C程序,而这正是编译器警告你的。
答案 3 :(得分:1)
int a = 42;
a = a = a;
是未定义的行为。
编写序列点规则是为了简化编译器制造商的工作。
答案 4 :(得分:0)
C标准没有规则说“如果行为不明确,那么行为是不明确的。”C 1999中的实际规则说“在上一个和下一个序列点之间,一个对象应该有它的通过表达式的评估,最多修改一次存储值。此外,先前的值应该是只读的,以确定要存储的值。“
您的代码违反了此规则:它修改了a
的值。 (3.1 3中的注释说“修改”包括存储的新值与前一个值相同的情况。)
就是这样。是否可以为此代码找出明确的解释并不重要。它只会违反规则。因为它违反了规则,所以行为未定义。
在C 2011中,该规则以更技术性的方式陈述。 6.5 2说“如果对标量对象的副作用相对于对同一标量对象的不同副作用或使用相同标量对象的值进行的值计算未进行排序,则行为未定义。如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是未定义的。“当赋值运算符在对象中存储值时,实际上是侧效果。 (主要效果是它评估存储的值。)因此,C 2011中的这个规则与C 1999规则大致相同:您可能不会对同一个对象产生两个副作用。
答案 5 :(得分:0)
您很可能最终获得所需的行为。当某人写a=a=a
时,他可能希望a
保持不变,当他写a=a=b
时,他可能希望a
在b
结束时更改为a=a=a
。声明。
然而,有可靠的硬件和软件组合确实打破了这一假设。考虑例如具有显式并行指令流的硬件。然后可以将双重赋值编译为试图将数据同时存储在同一寄存器中的两条指令。此外,硬件设计人员也可以做出这样的假设,即不允许执行该操作的指令对,并且可以对这些情况使用不关心值(并简化HW)。
然后,您实际上可能会遇到a
实际更改a=a=b
的值且a
最终b
不等于{{1}}的情况}。