C#多重赋值中中间值的损失
给定…C#多重赋值中中间值的损失,c#,C#,给定… int a = 1, b = 4; a += b += a += b; (a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4])) (a += (b += (a += b))); // a = 10[1 + 9] (b = 9[4 + 5] (a = 5[1 + 4])) // ^ re-use of original valu
int a = 1, b = 4;
a += b += a += b;
(a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))
(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)
然后…
int a = 1, b = 4;
a += b += a += b;
(a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))
(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)
<强>在C++ + < /强>
中进行了评估。int a = 1, b = 4;
a += b += a += b;
(a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))
(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)
。。。在C#中……
int a = 1, b = 4;
a += b += a += b;
(a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))
(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)
以上内容已经在VisualStudio2008和2012中进行了测试,因此不存在最近引入的语言错误的怀疑
然而,我确实希望C#的行为能模仿C++,并假设我需要教育。我理解表达式求值逻辑的方式,不需要解释MSIL。在进行了相当广泛的搜索之后,在语言规范中没有找到相关的细节,我希望语言专家能够解释为什么会这样
对于那些想知道我到底为什么要这么做的好奇的人。。。有一个简单的C++技巧,用于两个整数类型的一个非常高效和整洁的内联交换,这是这样的……BR>
我很失望地发现它在C#中不起作用,也很好奇为什么。这是关于理解C#的低级机制及其背后的原理。请不要多,不要少,特别是不要易读
NB
请各位,这不是一个认真的努力,在挤压到一行上的一切对于C(以及更高版本的C++),操作符的优先级和结合性总是被精确定义的。 赋值运算符的标准从右向左关联性使C/C++版本的行为100%清晰且可预测。它对我在几个编译器和平台上起作用,我会考虑一个没有这样的编译器出错。 我承认这行代码是模糊的,这是一种好奇心,我可能会给初级C/C++程序员一个“脑筋急转弯”。 显然,C#是不同的——而且显然是有意的。
我试图解释导致C#的行为与它的一种祖先语言不同的设计考虑。即使是受过教育的猜测也会受到欢迎 回答 感谢亚历山大·斯捷潘尼乌克。
从操作数的左到右求值开始
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;
我不喜欢它,因为它打破了我的直觉;-) 它也不能保证在C/C++中工作。在单个语句中多次赋值的变量值未定义 它能否工作取决于实现——您可能会发现编译器或体系结构根本不起作用 C#只是对它要求严格,这不是坏事
无论如何,xor技巧在现代体系结构上是毫无意义的。与仅使用临时变量相比,底层实现的效率更低。现代CPU上有2个以上的寄存器。甚至可以追溯到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!
有关更多未定义的行为,请参见此处:您实际上已经自己解释了所有内容:) “a(1)原值的重复使用” 为什么会这样? 你可以找到很好的解释 简而言之
a+=anything
等同于a=a+anything
所以编译器需要计算表达式
a+任何内容的两个操作数,然后将结果分配给a
:
1.计算第一个操作数(即a
)。评估结果为1
2.计算第二个操作数(即表达式
)。评价结果为9。评估的副作用是a
包含5个
3.计算第一个操作数和第二个操作数之和。结果是10
4.结果被分配给a
。现在a
包含10个
希望有帮助
更新
根据:表达式中操作数的求值顺序为从左到右。参见第150页的§14.2。
因此,这种行为在C#中是指定和正确的。如果您希望获得这些类型的性能提升,那么您使用C#到底是为了什么??我很欣赏你的好奇心,但无论如何,这对任何现代建筑来说都不是一个性能提升。它可能是慢的,并且一个错误等待发生。在C++中,优先级和关联性可能被重复,评价顺序不是。C/C++中赋值操作符的右向左结合性定义良好,并且在不同优先级(+,[])的操作符中,我的示例不混合。.你可能教了我一些东西。我一直都明白赋值链式(在C++中)的工作原理是准确的(而且只是),因为赋值语句总是返回其赋值的规范。我不知道有一条规则禁止在一条语句中对单个变量进行多个赋值。你有这条新闻的来源/链接吗?@AlanK“sequence points”我的问题不是发生了什么,而是为什么?为什么C#从右向左工作,所以。。。为a分配一个新值,然后使用a的新值为b分配一个新值,然后将a的旧值与b的新值相加以进行最终分配?对于赋值,C#运算符优先级和关联性规则与C/C++相同。那么为什么会有行为上的差异呢?我已经参考规范更新了我的答案。C#从左到右计算二进制表达式,这就是为什么使用了a
的旧值。谢谢。这就是我需要的。我已经根据这个新的理解更新了我原来的问题