【问题标题】:Why does this (i = ++i % 3) generate a warning: "may be undefined"? [duplicate]为什么这 (i = ++i % 3) 会生成警告:“可能未定义”? [复制]
【发布时间】:2011-10-24 14:33:54
【问题描述】:
int main(void)
{
    int i = 0;
    i = ++i % 3;
    return 0;
}

我是这样编译的:

$ gcc -Wall main.c -o main
main.c: In function ‘main’:
main.c:4: warning: operation on ‘i’ may be undefined

为什么编译器会说i 可能未定义?

【问题讨论】:

  • 操作 未定义,而不是 i 本身。 IE。这是未定义的行为。不要这样做。
  • @Paul R 为什么你认为操作是未定义的? ++i 将 i 递增到 1,并且以 3 为模运算器 1 给出 1 作为结果。这行不通吗?
  • @niko:看我的回答和catb.org/jargon/html/N/nasal-demons.html
  • @niko:请参阅下面 Fred 的答案以及他的答案中的链接,以进一步解释序列点等
  • @niko:我几乎不会将 gcc 称为“奇怪的实现”。对我来说似乎很受尊重,就这个问题而言,它完全符合标准。

标签: c


【解决方案1】:

因为您要修改i 的值两次,而没有干预sequence point。我是undefined behavior

【讨论】:

    【解决方案2】:

    在标准语言中,这是未定义的行为,因为 i 被修改了两次而没有插入序列点。

    i = ++i % 3;
    

    但这并不是重点。真正的重点是:为什么会有人写这样的代码?!

    您希望i 具有什么价值?如果你用i = ...i 分配一个全新的值,你想用++i 达到什么效果?如果这是并行宇宙 C,其中这样的代码实际上具有意义,那么 - 在最好的情况下 - 递增的 i 立即被分配的全新值替换。那么为什么不直接写成

    i = (i+1) % 3;
    

    这在 C-as-we-know-it 中也是正确的。

    【讨论】:

    • +1 表示“为什么会有人写这样的代码?!”
    • +1 表示“为什么会有人写这样的代码?!” :)
    【解决方案3】:

    正如其他人指出的那样,行为是undefined

    6.5 表达式
    ...
    2 在上一个序列点和下一个序列点之间,一个对象应该有它的存储值 通过表达式的评估最多修改一次。72) 此外,先验值 应为只读以确定要存储的值。73)
    ...
    72) 浮点状态标志不是对象,可以在表达式中多次设置。 73) 本段呈现未定义的语句表达式,例如
        我=++我+1;
        a[i++] = i;
    
    同时允许 我 = 我 + 1; a[i] = i;

    表达式i = ++i % 3 尝试在下一个序列点之前修改i 中包含的值两次(在本例中,; 结束语句),一次通过评估@987654325 @,并通过评估较大的赋值表达式进行一次。

    现在,为什么会有这样的问题?毕竟,C# 和 Java 可以很好地处理这些表达式。

    问题在于,除了少数例外,C 不保证表达式中的操作数以任何特定顺序计算,或者表达式的副作用将在计算表达式后立即应用(与 C# 和Java,确实做出了这些保证)。例如,表达式++i 有一个结果 (i + 1) 和一个副作用(增加存储在i 中的值);然而,副作用可以推迟到更大的表达式被计算之后。 IOW,允许执行以下操作序列:

    t0 = 我 + 1 t1 = t0 % 3 我 = t1 我 = 我 + 1

    哎呀。不是我们想要的。

    这是一个深思熟虑的设计决定;这个想法是它允许编译器以最佳方式重新排序评估(例如,通过利用已经在寄存器中的值)。缺点是某些表达式组合会产生不可预知的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-09
      • 1970-01-01
      • 2011-10-06
      • 2020-05-10
      • 2022-07-13
      • 1970-01-01
      • 2017-02-15
      相关资源
      最近更新 更多