为什么这个代码示例在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#中表现不同。
[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++中未定义。你可以得到任何结果。
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++ 代码可以做任何事情。arr[index] = ++index;
调用未定义的行为。
在 C++ 中未指定在同一赋值中使用 index 和 ++index 的行为。你不能只是这样做:arr[index] = index + 1
在那之后编写并增加你的变量。就此而言,在我的机器上使用我的 C++ 编译器时,我看到 arr[0] = 1,并且 arr[1] 没有受到影响。
至少在 C++ 的情况下,您通过预先递增和使用来调用未定义的行为,index
而两者之间没有序列点。如果您将该代码提供给 GCC 并启用警告,它将显示:
preinc.cpp:6: warning: operation on ‘index’ may be undefined
我猜它在 C# 中也是未定义的,但我不知道这种语言。至少对于 C 和 C++,答案是编译器可以做任何它想做的事情而不会出错,因为你的代码是错误的。不同的编译器(甚至同一个编译器)也没有义务产生一致的结果。
注意:根据@Eric Lippert的回答,该行为是为 C# 严格定义的,所以让我改写我的回答。
这段代码:
arr[index] = ++index;
即使 C# 编译器确切地知道如何评估它以及以何种顺序评估它,也很难阅读。仅出于这个原因,就应该避免它。
C# Operators 上的MSDN 页面甚至指出这种行为可能是未定义的,尽管 Eric 指出它不是。多个文档来源(但我相信 Eric 会对此有所不同)这一事实也表明这可能是最好的事情。
C++ 版本的结果并不总是如您在调用未定义行为时所编写的那样。在 C++ 中,如果您在表达式中使用变量的值,同时该变量也被修改了相同的表达式,您将获得未定义的行为,除非读取该值是确定要写入的值的一部分,或者表达式包含之间的序列点读和写。
在您的表达式中,您正在读取 的值index
以确定将 右侧的结果分配到何处=
,但右侧的子表达式也会修改index
.
C# 中的 index 是一种值类型,这意味着您在对其执行操作时会返回该值的新实例。
如果你把它想象成一个过程而不是一个操作符,这个过程应该是这样的:
public int Increment(int value)
{
int returnValue=value+1;
return returnValue;
}
然而,C++ 对对象的引用起作用,因此过程如下所示:
int Increment(int &value)
{
value=value+1;
return value;
}
注意:如果您一直在对象上应用运算符(比如重载了 ++ 运算符),那么 C# 的行为将与 C++ 类似,因为对象类型作为引用传递。