【问题标题】:Assignment and sequence points: how is this ambiguous?分配和序列点:这怎么模棱两可?
【发布时间】:2012-11-22 17:57:42
【问题描述】:

考虑 C 代码a = a = a。没有分配的序列点,因此在编译 a 上的未定义操作时,此代码会产生警告。

a 在这里可能有哪些值?似乎a 不可能改变值。这里实际上存在未定义的行为还是编译器只是懒惰?

【问题讨论】:

  • ... 更重要的是 - 你只是在傻吗?需要泼更多的水。
  • 假设我有一种可以编译成 C 的语言,而一个优化步骤意外地产生了这个......

标签: c compiler-construction sequence-points


【解决方案1】:

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

【讨论】:

  • 为什么不定义这种行为?只是为了避免在语言中添加另一个序列点?
【解决方案2】:
int a = 42;
a = a = a;

是未定义的行为。

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

【讨论】:

    【解决方案3】:

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

    “未定义的行为”实际上只是意味着 C 标准不再对程序的功能施加任何限制。这并不能阻止您推理特定编译器在看到该代码时可能会如何表现,但它仍然不是有效的 C 程序,这就是编译器警告您的原因。

    【讨论】:

    • 我想这个问题最好重述为:a=a=a 的语义应该是很明显的,那么不实现这些的技术原因是什么?
    • @Espressofa:此类可能的案例数不胜数,没有充分的理由打扰。
    【解决方案4】:

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

    人们可能会争辩说,这种“非修改修改”不应该引起任何问题。但是语言规范本身并不关心这些细节。同样,在语言术语中,每次您将某些内容写入变量时,您都在修改它。

    此外,您在问题中使用“模棱两可”一词似乎暗示您认为该行为是未指定。 IE。如“变量的结果值是(或不是)模棱两可的”。然而,在序列点违规中,语言规范并不局限于声明结果是未指定。它更进一步并声明了行为undefined。这意味着这些规则背后的基本原理不仅仅考虑某些变量的不可预测的最终值。例如,在一些假想的硬件平台上,非序列化修改可能会导致编译器生成无效代码,或类似情况。

    【讨论】:

    • 在编写 C 标准时,作者期望编译器编写者将未定义行为解释为意味着编译器编写者不需要保护程序员免受他们的行为可能在目标执行平台上造成的任何后果.我没有看到任何证据表明他们打算让编译器在直接生成的代码的行为本来有用但标准没有要求的情况下会不遗余力地生成无用的代码,但现在这似乎很流行。
    • @AnT 你能指出标准中的哪个地方说这有未定义的行为吗?
    【解决方案5】:

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

    您的代码违反了这条规则:它修改了a 的值。 (3.1 3 的注释说“修改”包括存储的新值与以前的值相同的情况。)

    原来如此。您是否可以为这段代码找出一个明确的解释并不重要。重要的是它违反了规则。因为它违反了规则,所以行为是未定义的。

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

    【讨论】:

    • 我在 C99 标准中找不到该文本。你能指出你指的是哪一段吗?
    • @skyking:“Between the previous and next...”文本位于 1999 C 标准(特别是 ISO/IEC 9899:1999(E))的第 6.5 条第 2 段中。
    【解决方案6】:

    您很可能最终会得到想要的行为。当有人写a=a=a 时,他可能希望a 保持不变,而当他写a=a=b 时,他可能希望在语句结束时将a 更改为b

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

    那么您实际上可能会遇到a=a=a 实际更改a 的值而a=a=b 最终导致a 不等于b 的情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-21
      • 2018-06-01
      • 1970-01-01
      相关资源
      最近更新 更多