3

鉴于...

int a = 1, b = 4;

然后...

a += b += a += b;  

在 C++ 中评估...

(a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  

...在 C# 中...

(a += (b += (a += b))); // a = 10[1 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  
                        //        ^ re-use of original value of a (1)  
                        //          instead of expected intermediate right-to-left  
                        //          evaluation result (5)  

以上内容已经在 Visual Studio 2008 和 2012 中测试过,因此不怀疑最近引入的语言错误。

但是,我确实希望 C# 的行为能够模仿 C++,并假设我需要接受教育。我了解表达式评估逻辑的 HOW,不需要 MSIL 解释。在进行了相当广泛的搜索后,未能在语言规范中找到相关的细则,我希望语言专家能解释为什么会这样。

对于那些想知道我到底为什么要这样做的好奇者......有一个方便的小 C++ 技巧,可以对两种整数类型进行非常高效和整洁的内联交换,就像这样......

a ^= b ^= a ^= b;  

我很失望地发现它在 C# 中不起作用并且很好奇为什么。这是关于理解 C# 的低级机制及其背后的基本原理。没有更多,没有更少,特别是没有可读性宗教。

请注意 ,伙计们,这不是把所有东西都挤在一条线上的认真努力!!!
对于 C(和后来的 C++),运算符优先级和关联性总是被精确定义。赋值运算符的标准从右到左关联性使 C/C++ 版本的行为 100% 清晰且可预测。它在几个编译器和平台上都对我有用,我认为没有像这样表现的编译器是有问题的。
我承认代码行被混淆了,这是一种好奇心,并被用作我可能给初级 C/C++ 程序员的“脑筋急转弯”。显然 C# 是不同的——而且显然是故意的。
我寻求对导致 C# 与其一种祖先语言的行为分歧的设计考虑进行解释。即使有根据的猜测也会受到欢迎。

感谢 Alexander Stepaniuk 的回答。
从操作数的从左到右求值开始

a        = 1 + theRest1     // (1)  
theRest1 = b = 4 + theRest2 // (2)  
theRest2 = a = 1 + 4        // (3)  
(3) into (2): theRest1 = b = 4 + 5  
(2) into (1): a = 1 + 9  

“为什么”的解释仍然值得赞赏。
但 C# 规范清楚,以上是正确的评估。
下面是尽可能接近(我认为)使用 2 个变量的 C++ 技巧......

b ^= a ^= b;
a ^ = b;

我不喜欢它——为了记录——因为它打破了我的直觉;-)

4

3 回答 3

3

它甚至可以追溯到c。

K&R 有这个例子:

a[i++] = i;

这是未定义的行为,在一行中修改和使用变量。一种编译器可以一种方式工作,另一种方式工作。该标准故意将其保留为未定义,以便您不依赖它。

K&R 建议使用单独的行或中间变量。不是为了人类可读性,而是为了使编译器的解释明确。

a ^= b ^= a ^= b;

所以这变得明确:

a ^= b; b ^= a; a ^= b; // can still on one line if that is important to you!

有关更多未定义行为,请参见此处:为什么这些构造(使用 ++)未定义行为?

于 2012-11-24T11:06:31.180 回答
3

也不能保证在 C/C++ 中工作。在单个语句中多次分配的变量的值是未定义的。

它的工作原理取决于实现——你不妨找到一个根本不起作用的编译器或架构。

C# 只是对它严格,这不是坏事。

无论如何,异或技巧在现代架构上毫无意义。底层实现的效率将低于仅使用临时变量。现代 CPU 上有超过 2 个寄存器。

于 2012-11-24T10:53:15.327 回答
2

你实际上已经自己解释了一切:)

《再利用一(1)的原值》

为什么会这样?你可以在这里找到很好的解释

总之
a += anything相当于a = a + anything
因此编译器需要计算表达式的两个操作数,a + anything然后将结果分配给a
1. 计算第一个操作数(即a)。评估结果为 1
2。评估第二个操作数(即expression)。评估的结果是 9。评估的副作用是a包含 5
3。评估第一个和第二个操作数的总和。结果是 10
4. 结果分配给a。现在a包含 10 个。

希望有帮助。

更新
根据c# 规范计算表达式中的操作数的顺序是从左到右请参阅第 150 页的 §14.2。
因此,此行为在 C# 中已指定并正确。

于 2012-11-24T16:03:41.250 回答