【问题标题】:Is (--i == i++) an Undefined Behavior?(--i == i++) 是未定义的行为吗?
【发布时间】:2011-03-14 04:54:03
【问题描述】:

这个问题与我之前的problem 有关。我得到的答案是“这是一种未定义的行为。”

请任何人解释:

  • 什么是未定义行为?
  • 我如何知道我的代码有未定义的行为?

示例代码:

int i = 5;
if (--i == i++)         
   Console.WriteLine("equal and i=" + i);           
else
   Console.WriteLine("not equal and i=" + i);

//output: equal and i=6

【问题讨论】:

  • 您使用什么语言? C# 和 C 非常不同
  • 呃,C 还是 C#?这不是有效的 C,为什么要添加标签?
  • "什么是未定义行为?"它的 3 或 428,3 取决于附近的重力和发电厂。
  • 你为什么要这样写,让你的程序员同事感到困惑?
  • Console.WriteLine() 在 C 中做什么?

标签: c# undefined-behavior


【解决方案1】:

什么是未定义行为?

这很简单,任何没有被适当的语言规范明确定义的行为。一些规范会将某些内容明确列出为未定义,但实际上任何未定义为未定义的内容都是未定义的。

我如何知道我的代码有未定义的行为?

希望您的编译器会警告您 - 如果不是这样,您需要阅读语言规范并了解导致此类问题的所有有趣的极端情况以及角落和缝隙。

在外面小心点!

【讨论】:

    【解决方案2】:

    它在 C 中未定义,但在 C# 中定义良好:

    来自 C# (ECMA-334) 规范“运算符优先级和关联性”部分(第 14.2.1 节):

    • 除了赋值运算符和空合并运算符,所有 二元运算符是左- 关联的,意思是操作 从左到右执行。 [示例:x + y + z 被评估为 (x + y) + z。结束示例]

    所以首先评估--i,将i 更改为4 并评估为4。然后i++ 进行评估,将i 更改为5,但评估为4。

    【讨论】:

    • 那句话出自哪里? C 还是 C#?
    • 它的定义是正确的,但这不是它的定义位置或方式。它既不是赋值也不是条件操作。
    • 问题不在于关联性或优先级,这两者在 c 和 c++ 中也都有很好的定义。问题在于,在函数调用(或运算符)中的 c(++) 中,您无法保证在序列点之间评估操作数的顺序。由于您只有一个二元运算符,因此关联性在这里甚至无关紧要。重要的是 c# 指定从左到右评估所有操作数,包括副作用(例如赋值操作)。你可以在这里找到更多相关信息blogs.msdn.microsoft.com/oldnewthing/20070814-00/?p=25593
    【解决方案3】:

    是的,该表达式也是未定义的行为(在 C 和 C++ 中)。有关规则的一些信息,请参阅http://en.wikipedia.org/wiki/Sequence_point;您还可以更一般地搜索“序列点”(即您的代码违反的一组规则)。

    【讨论】:

      【解决方案4】:

      (假设使用 C 或 C++。)

      Carl 的回答总体上是准确的。

      具体来说,问题出在耶利米所指出的:sequence points

      为了澄清,代码块(--i == ++i)是一个单一的“发生”。这是一个一次性评估的代码块。首先发生的事情没有明确的顺序。可以先评估左侧,也可以评估右侧,或者比较相等性,然后 i 递增,然后递减。这些行为中的每一个都可能导致此表达式具有不同的结果。这里会发生什么是“未定义的”。你不知道答案会是什么。

      将此与语句 i = i+1; 进行比较在这里,总是首先计算右侧,然后将其结果存储到 i 中。这是很好定义的。没有歧义。

      希望能有所帮助。

      【讨论】:

        【解决方案5】:

        在 C 中结果是未定义的,在 C# 中是已定义的。

        在 C 中,比较被解释为:

        按照任意顺序执行所有这些操作:
        - 减少i,然后将i 的值变成x
        - 获取i的值到y,然后增加i
        然后比较 x 和 y。

        在C#中操作边界比较多,所以比较解释为:

        减少i
        然后将 i 的值放入 x
        然后将i 的值放入 y
        然后增加i
        然后比较 x 和 y。

        由编译器选择在操作边界内执行操作的顺序,因此将矛盾的操作放在同一边界内会导致结果不确定。

        【讨论】:

          【解决方案6】:

          因为 C 标准如此规定。而且您的示例清楚地显示了未定义的行为。

          根据评估顺序,比较应该是4 == 55 == 6。然而条件返回 True。

          【讨论】:

            【解决方案7】:

            您之前的问题被标记为 [C],所以我根据 C 来回答,即使您当前问题中的代码看起来不像 C。

            C99 中未定义行为的定义说(§3.4.3):
            1 未定义的行为
            行为,在使用不可移植或错误的程序构造或错误数据时, 本国际标准对此没有要求

            2 注意 可能的未定义行为范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式表现(有或没有发出诊断消息),到终止翻译或执行(发出诊断消息)。

            C 标准的附录 J.2 有一个(长——几页)未定义行为的列表,尽管这仍然不是详尽无遗的。在大多数情况下,未定义的行为意味着您违反了规则,因此了解它的方法就是了解规则。

            【讨论】:

              【解决方案8】:

              未定义的行为 == 无法保证无论何时在完全相同的条件下运行结果始终相同,或者无法保证无论何时使用不同的编译器或运行时执行结果始终相同。

              在您的代码中,由于它使用了一个相等的比较运算符,它没有指定应该首先执行操作数的哪一侧--ii++ 可能最终会首先运行,并且您的答案将取决于编译器的实际实现。如果先执行--i,则为4 == 4,i=5;如果先实现i++,则为5 == 5,i=5。

              答案可能相同的事实并不能阻止编译器警告您这是一个未定义的操作。

              现在,如果这是一种定义始终应首先执行左侧(或右侧)的语言,则该行为将不再是未定义的。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-05-19
                • 1970-01-01
                • 2011-04-25
                • 2013-07-03
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-02-15
                相关资源
                最近更新 更多