【问题标题】:Index, assignment and increment in one statement behaves differently in C++ and C#. Why?一条语句中的索引、赋值和增量在 C++ 和 C# 中的行为不同。为什么?
【发布时间】:2010-12-09 01:11:24
【问题描述】:

为什么这个代码示例在 c++C# 中表现不同。

[C++ 示例]

int arr[2];
int index = 0;
arr[index] = ++index;

其结果将是 arr[1] = 1;

[C# 示例]

int[] arr = new int[2];
int index = 0;
arr[index] = ++index;

其结果将是 arr[0] = 1;

我觉得这很奇怪。当然,两种语言都必须有一些理由以不同的方式实现它吗?我想知道 C++/CLI 会输出什么?

【问题讨论】:

    标签: c# c++ operators indexing prefix-operator


    【解决方案1】:

    正如其他人所指出的,此代码的行为在 C/C++ 中是未定义。你可以得到任何结果。

    C# 代码的行为由 C# 标准严格定义

    肯定有一些理由让两种语言以不同的方式实现它吗?

    好吧,假设您正在设计 C#,并希望使 C++ 程序员易于学习该语言。你会选择复制 C++ 解决这个问题的方法,即不定义它吗?你真的想让完全聪明的开发人员很容易意外地编写编译器可以为它想要的任何含义的代码吗?

    C# 的设计者不相信简单表达式的未定义行为是一件好事,因此我们严格定义了这样的表达式的含义。我们不可能同意每个 C++ 编译器所做的事情,因为不同的 C++ 编译器会为您提供此类代码的不同结果,因此我们无法同意所有这些。

    至于为什么 C++ 的设计者认为最好让这样的简单表达式具有未定义的行为,好吧,你必须问其中一个。我当然可以做出一些猜想,但这些只是有根据的猜测。

    我已经写了很多关于这类问题的博客文章;我最近的一个几乎就是你在这里提到的代码。您可能想阅读的一些文章:

    C# 的设计如何鼓励消除细微的错误:

    http://blogs.msdn.com/ericlippert/archive/2007/08/14/c-and-the-pit-of-despair.aspx

    C#中的优先级、关联性和执行顺序之间究竟是什么关系?

    http://blogs.msdn.com/ericlippert/archive/2008/05/23/precedence-vs-associativity-vs-order.aspx

    索引、赋值和递增的副作用是按什么顺序发生的?

    http://blogs.msdn.com/ericlippert/archive/2009/08/10/precedence-vs-order-redux.aspx

    【讨论】:

    • 它在 C++ 中是未定义的,因为它在 C 中也是未定义的。显然,不同的 C 编译器在采用标准之前做了不同的事情,恕我直言,唯一明智的选择是让它未定义。
    【解决方案2】:

    实际上,您的 C++ 代码可以做任何事情。 arr[index] = ++index; 调用未定义的行为。

    【讨论】:

      【解决方案3】:

      在同一赋值中使用 index 和 ++index 的行为在 C++ 中未指定。你不能这样做:写arr[index] = index + 1 然后增加你的变量。就此而言,在我的机器上使用我的 C++ 编译器时,我看到 arr[0] = 1,并且 arr[1] 没有受到影响。

      【讨论】:

      • 它比未指定更糟糕,它实际上是未定义的行为。
      • 当然,这就是我应该写的(它指定undefined ;-)当你花了一个整个上午调试......(我什至在 gdb 中发现了一个错误!)
      【解决方案4】:

      至少在 C++ 的情况下,您通过预递增和使用 index 来调用未定义的行为,而两者之间没有序列点。如果您将该代码提供给 GCC 并启用警告,它将显示:

      preinc.cpp:6: warning: operation on ‘index’ may be undefined
      

      我猜它在 C# 中也是未定义的,但我不知道该语言。至少对于 C 和 C++,答案是编译器可以为所欲为而不会出错,因为您的代码是错误的。不同的编译器(甚至同一个编译器)也没有义务产生一致的结果。

      【讨论】:

      • 出于好奇,G++ 4.4.1 的行为类似于 C#,设置 array[0] = 1。
      • 未定义的行为意味着它可以设置array[1]=1或设置array[4711]=42,擦除你的硬盘,或者让你秃头,这取决于调试器是否存在,它正在运行第42次,看月相,看你女朋友有多喜欢你,或者你有没有对你妈不顺眼。说“编译器 x,版本 y,this 方式”是没有意义的,因为你永远不知道。 (stackoverflow.com/questions/1553382/1553407#1553407)
      • 它在 C# 中严格定义。我们不参与这种疯狂的业务,即拥有没有意义的简单表达式,但编译器无论如何都会接受它们并做一些疯狂的事情。在这里给我们一些信任!
      • '疯狂的生意'?当“未定义行为”的含义点击时,埃里克可能会重新审视这一点。问题是您解决了一个问题,并引入了另一个问题,并且简单地将 30y 向后兼容性称为“疯狂”行为的罪魁祸首;这使得它比在“新”语言中采用 C 风格的家族语法更糟糕。C# 和它的编译器是如此强大和新,它开始看起来像 C++,一路没有指针,每次迭代。请寻找应得的功劳:Compiler that compiles your C-style Java copycat VM and C# compiler。
      【解决方案5】:

      注意:根据@Eric Lippertanswer,该行为是针对C# 严格定义的,所以让我改写我的答案。

      这段代码:

      arr[index] = ++index;
      

      即使 C# 编译器确切地知道如何评估它以及以何种顺序评估它,也很难阅读。仅出于这个原因,就应该避免它。

      C# Operators 上的MSDN page 甚至指出这种行为可能是未定义的,尽管 Eric 指出它不是。多个文档来源(不过我相信 Eric 会对此有所不同)这一事实也表明这可能是最好的事情。

      【讨论】:

      • 它在 C# 中不是未定义的,它是严格定义的。见上文。
      • 也许应该有人去更新 MSDN 文档?
      • 天哪,该页面充满了错误。我将立即与文档经理交谈。感谢您提请我注意。
      • 我建议召开 C# 语言设计会议,将“未定义的行为”明确为一个很好理解的术语。
      • 未定义行为实际上是一个定义非常明确的术语。这并不意味着“随机”或“每次都不同”,而是意味着无论您观察到代码正在做什么,都不是您将来应该依赖它来做的事情。例如,移动代码或更改优化参数可能会改变行为。
      【解决方案6】:

      C++ 版本的结果并不总是如您在调用未定义行为时编写的那样。在 C++ 中,如果您在表达式中使用变量的值,同时该变量也被修改相同的表达式,您将得到 未定义的行为,除非读取该值是确定要写入的值的一部分,或者表达式包含读取和写入之间的序列点

      在您的表达式中,您正在读取index 的值以确定将= 右侧的结果分配到何处,但右侧子表达式也修改了index

      【讨论】:

        【解决方案7】:

        C#中的索引是一个值类型,这意味着当你对它执行操作时,你会返回一个新的值实例。

        如果你把它想象成一个过程而不是一个操作符,这个过程应该是这样的:

        public int Increment(int value)
        {
           int returnValue=value+1;
           return returnValue;
        }
        

        但是,C++ 对对象的引用起作用,因此过程如下所示:

        int Increment(int &value)
        {
           value=value+1;
           return value;
        }
        

        注意:如果您一直在对对象应用运算符(比如重载了 ++ 运算符),那么 C# 的行为将与 C++ 类似,因为对象类型作为引用传递。

        【讨论】:

        • 你是说C#中的++index不修改索引吗?
        • 不,我只是因为我试图解释而不是思考而被剥夺了睡眠
        猜你喜欢
        • 1970-01-01
        • 2018-09-07
        • 1970-01-01
        • 1970-01-01
        • 2011-07-08
        • 1970-01-01
        • 1970-01-01
        • 2019-09-15
        • 1970-01-01
        相关资源
        最近更新 更多