首页 文章

c - 为什么i = i调用未定义的行为? [重复]

提问于
浏览
3

这个问题在这里已有答案:

我理解C使用序列点的概念来识别模糊计算,并且 = 运算符不是序列点 . 但是,我无法看到执行该语句时有任何歧义

i = ++i

根据我的理解,这仅仅是评估 &i 处的任何内容,增加它并将其存储在同一位置 . 然而,GCC将其标记为:

[警告]'i'上的操作可能未定义[-Wsequence-point]

我错过了 = 功能的一些内容吗?

编辑:在标记为重复之前,请注意我已经浏览了有关序列点和未定义行为的其他帖子 . 它们都没有特别针对表达式 i=++i (注意预增量) . 提到的表达通常是 i=i++a=b++ + ++b 等 . 我对它们中的任何一个都毫无疑问 .

2 回答

  • 5

    你遗漏了一些未定义的行为 . 未定义的行为只是意味着编译器可以做任何想做的事情 . 它可以抛出错误,它可以(如GCC所示)显示警告,它可以导致恶魔飞出你的鼻子 . 主要的是,它不会表现良好,它赢得了't behave consistently between compilers, so don' t!

    在这种情况下,编译器不必使得在返回语句的rhs之前必须完成运算符的lhs的副作用 . 这对你来说很有趣,但你不像计算机那样思考 . 如果需要,它可以计算返回值并将其返回到寄存器中,将其分配给i,然后对实际值执行增量 . 所以它看起来更像

    register=i+1;
    i=register;
    i=i+1;
    

    该标准不保证不会发生这种情况,所以就是不要这样做!

  • 2

    出现未定义的行为是因为变量 i 在两个sequence points之间被多次修改 . 序列点是之前评估的所有副作用都可见的点,但未来的副作用是不可见的 . 标准规定:

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

    那么,我们关注的副作用是什么?

    • ++i ,它为我赋值 i+1

    • i = ++i ,它为i赋予表达式 ++i 的值,即 i+1

    因此,我们将得到两个(公认的,等效的)副作用:将 i+1 分配给变量 i . 我们关注的是,在这两个序列点之间发生这些副作用?

    什么操作构成序列点?有多个,但这里只有一个实际相关:

    • 在完整表达式的末尾(在这种情况下, i = ++i 是完整表达式)

    即,预增量 ++i is not a sequence point . 这意味着两个副作用(增量和赋值)将在相同的两个序列点之间发生,修改相同的变量 i . 因此,它是未定义的行为;两个修改碰巧具有相同 Value 的事实是无关紧要的 .


    但是为什么在序列点之间多次修改变量是不好的呢?为了防止这样的事情:

    i = ++i + 1;
    

    这里, i 递增,但由于预增量的语义,它也被赋值 (i+1) + 1 . 由于副作用具有模糊的排序,因此行为未定义 .

    现在,可以假设标准中的一个特殊情况是,只要值相同,两个序列点之间的多次修改就可以,但这可能会使编译器实现不必要地复杂化,而没有太大的好处 .

相关问题