对于i = 0,为什么(i + = i ++)等于0?
采取以下代码(可用作控制台应用程序):
static void Main(string[] args) { int i = 0; i += i++; Console.WriteLine(i); Console.ReadLine(); }
i
的结果是0.我期望2(正如我的一些同事所做的那样)。 编译器可能会创build某种结构,导致i
为零。
我期望2的原因是,在我的思路中,右手声明将首先被评估,以1递增i。比它增加到i。 因为我已经是1,所以加1到1.所以1 + 1 = 2。显然这不是发生了什么事情。
你能解释一下编译器在做什么或者在运行时发生了什么? 为什么结果为零?
某种forms的免责声明:我完全知道你不会(也可能不应该)使用这个代码。 我知道我永远不会。 不过,我觉得知道为什么它会以这样的方式行事,究竟发生了什么是很有趣的。
这个:
int i = 0; i += i++
可以看出你在做什么(以下是一个过分简单化):
int i = 0; i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed i + 1; // Note that you are discarding the calculation result
实际发生的事情比这更多 – 看一下MSDN 7.5.9后缀增量和减量运算符 :
后缀x或xforms的增量或减量操作的运行时处理由以下步骤组成:
如果x被分类为一个variables:
- 评估x来产生variables。
- x的值被保存。
- 所选运算符将以保存的x值作为参数进行调用。
- 操作员返回的值存储在x的评估给定的位置。
- x的保存值将成为操作的结果。
请注意,由于优先级的顺序 ,后缀++
出现在 +=
之前 ,但是结果不被使用(因为使用了前一个值)。
将i += i++
更加彻底地分解为它所构成的部分,需要知道+=
和++
不是primefaces的(也就是说,哪一个都不是单一的操作),即使它们看起来像它们一样。 这些实施的方式涉及临时variables,操作发生前i
副本 – 每个操作一个。 (我将使用名称iAdd
和iAssign
分别用于++
和+=
的临时variables)。
所以,更接近发生的事情是:
int i = 0; int iAdd = i; // Copy of the current value of i, for ++ int iAssign = i; // Copy of the current value of i, for += i = i + 1; // i++ - Happens before += due to order of precedence i = iAdd + iAssign;
运行代码的反汇编:
int i = 0; xor edx, edx mov dword ptr i, edx // set i = 0 i += i++; mov eax, dword ptr i // set eax = i (=0) mov dword ptr tempVar1, eax // set tempVar1 = eax (=0) mov eax, dword ptr i // set eax = 0 ( again... why??? =\ ) mov dword ptr tempVar2, eax // set tempVar2 = eax (=0) inc dword ptr i // set i = i+1 (=1) mov eax, dword ptr tempVar1 // set eax = tempVar1 (=0) add eax, dword ptr tempVar2 // set eax = eax+tempVar2 (=0) mov dword ptr i, eax // set i = eax (=0)
等效的代码
它编译为与以下代码相同的代码:
int i, tempVar1, tempVar2; i = 0; tempVar1 = i; // created due to postfix ++ operator tempVar2 = i; // created due to += operator ++i; i = tempVar1 + tempVar2;
第二个代码的反汇编(只是为了certificate它们是一样的)
int i, tempVar1, tempVar2; i = 0; xor edx, edx mov dword ptr i, edx tempVar1 = i; // created due to postfix ++ operator mov eax, dword ptr i mov dword ptr tempVar1, eax tempVar2 = i; // created due to += operator mov eax, dword ptr i mov dword ptr tempVar2, eax ++i; inc dword ptr i i = tempVar1 + tempVar2; mov eax, dword ptr tempVar1 add eax, dword ptr tempVar2 mov dword ptr i, eax
打开反汇编窗口
大多数人不知道,甚至不记得,他们可以看到最终的内存汇编代码,使用Visual Studio Disassembly窗口。 它显示正在执行的机器代码,它不是CIL。
debugging时使用这个:
Debug (menu) -> Windows (submenu) -> Disassembly
那么postfix ++发生了什么?
后缀++告诉我们,在评估之后,我们希望增加操作数的值……大家都知道……有点混淆是“评估之后”的意思。
那么“评估之后”是指:
- 操作数的其他用法,必须影响相同的代码行:
-
a = i++ + i
第二个i受到增量的影响 -
Func(i++, i)
第二我受到影响
-
- 在同一条线路上的其他用法就像
||
这样的短路操作 和&&
:-
(false && i++ != i) || i == 0
(false && i++ != i) || i == 0
第三我不受我的++,因为它没有评估
-
那么是什么意思: i += i++;
?
这与i = i + i++;
评估顺序是:
- 存储我+我(即0 + 0)
- 递增我(我变成1)
- 将步骤1的值分配给i(i变为0)
并不是说增量正在被丢弃。
是什么意思: i = i++ + i;
?
这与前面的例子不一样。 第三i
受到增量的影响。
评估顺序是:
- 存储我(即0)
- 递增我(我变成1)
- 步骤1 + i的存储值(即0 + 1)
- 将步骤3的值分配给i(i变为1)
int i = 0; i += i++;
评估如下:
Stack<int> stack = new Stack<int>(); int i; // int i = 0; stack.Push(0); // push 0 i = stack.Pop(); // pop 0 --> i == 0 // i += i++; stack.Push(i); // push 0 stack.Push(i); // push 0 stack.Push(i); // push 0 stack.Push(1); // push 1 i = stack.Pop() + stack.Pop(); // pop 0 and 1 --> i == 1 i = stack.Pop() + stack.Pop(); // pop 0 and 0 --> i == 0
即i
改变了两次:一次由i++
expression式,一次由+=
语句。
但是+=
语句的操作数是
- 在评估
i++
之前的值(+=
左侧)和 - 在评估
i++
之前的值(+=
右侧)。
首先, i++
返回0.然后i
递增1.最后, i
被设置为i
的初始值,它是0加上返回的值i++
,也是0。 0 + 0 = 0。
这是抽象语法树从左至右的自下而上的评估。 从概念上讲,expression式的树是从上到下走的,但是评价随着recursion从底部弹回树而展开。
// source code i += i++; // abstract syntax tree += / \ i ++ (post) \ i
评估从考虑根节点+=
。 这是expression的主要组成部分。 +=
的左操作数必须进行评估,以确定我们存储variables的位置,并获得先验值为零。 接下来,必须评估右侧。
右侧是一个后增加的++
运算符。 它有一个操作数, i
被评估为一个值的来源,并作为一个值将被存储的地方。 操作员评估i
,find0
,从而将1
存储到该位置。 它根据返回先验值的语义返回先验值0。
现在控制回到+=
操作符。 它现在有所有的信息来完成它的操作。 它知道存储结果的位置( i
的存储位置)以及先前值,并且具有添加到先前值的值,即0
。 所以, i
结束了零。
像Java一样,C#通过修改评估顺序来清理C语言的一个非常重要的方面。 从左到右,自下而上:编码人员可能预期的最明显的顺序。
因为i++
首先返回值,然后增加它。 但是,在我设置为1之后,您将其设置回0。
后增加方法看起来像这样
int ++(ref int i) { int c = i; i = i + 1; return c; }
所以基本上当你调用i++
, i
是增量的,但是在你的情况下返回原始值0。
简单的答案
int i = 0; i += i++; // Translates to: i = i + 0; // because post increment returns the current value 0 of i // Before the above operation is set, i will be incremented to 1 // Now i gets set after the increment, // so the original returned value of i will be taken. i = 0;
我++的意思是:返回我的价值,然后增加它。
i + = i ++表示:取i的当前值。 添加i ++的结果。
现在,我们添加i = 0作为开始条件。 i + = i ++现在被评估如下:
- 我现在的价值是多less? 它是0.存储它,所以我们可以添加i ++的结果。
- 评估我++(评估为0,因为这是我的当前值)
- 加载存储的值并将步骤2的结果添加到该值。 (加0到0)
注意:在步骤2结束时,i的值实际上是1.但是,在步骤3中,通过在递增之前加载i的值来丢弃它。
与i ++相反,++ i返回递增的值。
所以,我+ = ++我会给你1。
后缀增量运算符++
给expression式中的variables一个值,然后执行你指定的增量,返回的零(0)值再次覆盖增加的那个(1) ,所以你得到零。 您可以在++运算符 (MSDN)中阅读有关增量运算符的更多信息。
i += i++;
将等于零,因为它在++
之后。
i += ++i;
会做到这一点
++ postfix在递增之前计算i
,而+=
只计算一次。
因此,0 + 0 = 0,因为在增量之前i
被评估和使用,因为使用++
的后缀格式。 为了让i
先递增,使用前缀forms( ++i
)。
(另外,只是一个注释:你应该只得到1,作为0 +(0 + 1)= 1)
参考资料: http : //msdn.microsoft.com/en-us/library/sa7629ew.aspx (+ =)
http://msdn.microsoft.com/en-us/library/36x43w8w.aspx(++ )
C#正在做什么,以及混乱的“原因”
我也预计价值是1 …但是在这个问题上的一些探索确实澄清了一些观点。
Cosider有以下方法:
static int SetSum(ref int a, int b) { return a += b; } static int Inc(ref int a) { return a++; }
我期望, i += i++
是相同的SetSum(ref i, Inc(ref i))
。 我在这个陈述后的价值是1 :
int i = 0; SetSum(ref i, Inc(ref i)); Console.WriteLine(i); // i is 1
但是后来我得出了另一个结论… i += i++
实际上与i = i + i++
…所以我创build了另一个类似的例子,使用这些函数:
static int Sum(int a, int b) { return a + b; } static int Set(ref int a, int b) { return a = b; }
在调用Set(ref i, Sum(i, Inc(ref i)))
,i的值为0 :
int i = 0; Set(ref i, Sum(i, Inc(ref i))); Console.WriteLine(i); // i is 0
这不仅解释了C#正在做什么,而且也解释了为什么很多人对此感到困惑……包括我在内。
我总是记得一个很好的助记符如下:
如果++
在expression式之后,则返回它之前的值。 所以下面的代码
int a = 1; int b = a++;
是1,因为a
是1 之前,它是由++
之后增加a
。 人们称这个修复后的符号。 还有一个预定义符号,其中事情正好相反:如果++
代表之前 ,expression式返回操作之后的值:
int a = 1; int b = ++a;
b
在这里是两个。
所以对于你的代码,这意味着
int i = 0; i += (i++);
i++
返回0(如上所述),所以0 + 0 = 0
。
i += (++i); // Here 'i' would become two
Scott Meyers在“有效的C ++编程”中描述了这两个符号之间的区别。 在内部, i++
(postfix)会记住i
的值,并调用前缀记号( ++i
)并返回旧的值i
。 这就是为什么你应该总是使用++i
在for
循环(虽然我认为所有现代编译器正在翻译i++
到++i
在for
循环)。
你的问题的唯一答案是正确的: 因为它是未定义的。
好的,在你们全部烧我之前
你们都回答为什么i+=i++
是好的,并在逻辑上导致i=0
。
我被诱惑投了你的每一个答案,但我计算的声望打得太高了..
为什么我对你这么生气? 不是因为你的答案解释..
我的意思是,我读过的每一个答案都做了非凡的努力来解释不可能的事情,我鼓掌!
但是结果是什么? 这是直观的结果 – 是可以接受的结果?
你们每个人都看到了“裸体王”,并以某种方式接受了这个理性的国王。
你们都是错的!
i+=i++;
结果0
是未定义的。
语言评估机制中的一个错误,如果你会…或甚至更糟! devise中的一个错误。
想要certificate? 当然你想!
int t=0; int i=0; t+=i++; //t=0; i=1
现在这个…是直观的结果! 因为我们首先评估t
赋值,只有在评估和分配后,我们才会发生后期操作 – 理性是不是?
是理性的: i=i++
和i=i
对i
有同样的结果吗?
而t=i++
和t=i
对t=i
有不同的结果。
报表操作是在报表评估之后发生的事情。
因此:
int i=0; i+=i++;
如果我们写的话应该是一样的:
int i=0; i = i + i ++;
因此相同:
int i=0; i= i + i; i ++;
因此相同:
int i=0; i = i + i; i = i + 1;
任何不是1
结果都表明了编译器中的错误,或者如果我们用理性的思维去理解语言devise中的错误,MSDN和其他许多来源告诉我们“嘿 – 这是不确定的!
现在,在我继续之前,即使我给出的这套例子都不被任何人支持或承认。但是,这是根据直觉和理性的方式应该得到的结果。
编码人员不应该知道程序集是如何编写或翻译的!
如果它的写法不尊重语言定义 – 这是一个错误!
而完成我从维基百科复制这个, 增量和减量操作符 :
由于递增/递减运算符修改其操作数,所以在同一expression式中多次使用这样的操作数可能会产生未定义的结果 。 例如,在诸如x – ++ x的expression式中,不应该以什么顺序执行减法运算符和增量运算符。 当编译器应用优化时,这样的情况会变得更糟,这可能导致操作的执行顺序与程序员所期望的不同。
因此。
正确的答案是,这不应该被使用! (因为它是未定义的!)
是的 – 即使C#编译器试图以某种方式规范化,结果也是不可预测的。
我没有find任何C#文档来描述你们所有人的行为,这些行为logging为正常或定义明确的语言行为。 我所发现的恰恰相反!
[ 从MSDN文档复制后缀增量和减量运算符:++和 – ]
当将后缀运算符应用于函数参数时,参数值不保证在传递给函数之前递增或递减。 有关更多信息,请参见C ++标准中的第1.9.17节。
注意这些词不保证 …
原谅我,如果这个答案似乎傲慢 – 我不是一个傲慢的人。 我只是想,有成千上万的人来这里学习,我读到的答案会误导他们,会损害他们对这个问题的逻辑和理解。
variables之后的++运算符使其成为后缀增量。 递增发生在声明中的所有其他内容之后,即添加和分配。 相反,如果你把++放在variables之前,它会在我的值被评估之前发生,并给你预期的答案。
计算步骤是:
-
int i=0
//初始化为0 -
i+=i++
//等式 -
i=i+i++
//在通过编译器简化公式之后 -
i=0+i++
我值替代 -
i=0+0
// i ++是0 -
i=0
//最终结果i = 0
在这里,最初i
的值是0. WKT, i++
只不过是:首先使用i
值,然后将i
值递增1.因此,它使用i
值0,同时计算i++
,然后递增1。所以它的结果是0。
有两个选项:
第一个选项:如果编译器按如下方式读取语句,
i++; i+=i;
那么结果是2。
对于
else if i+=0; i++;
结果是1。
要非常小心:阅读C 常见问题解答 :你要做什么(混合分配和同一个variables的++
)不仅没有说明,而且也是未定义的(这意味着编译器在评估时可能做任何事情 ,而不是只给出“合理”的结果)。
请阅读第3节 。 整个部分非常值得一读! 特别是3.9,这说明了未指定的含义。 3.3节给你一个简短的总结,你可以做什么,不能做什么,用“i ++”等等。
取决于编译器的内部,你可能会得到0,或2,或1,甚至任何其他的东西! 因为它是不确定的,所以他们可以这样做。
希望从C编程101types的angular度来回答这个问题。
在我看来,这是按顺序发生的:
-
i
被评估为0,导致i = 0 + 0
,增量操作i++
“排队”,但是对于i
的赋值还没有发生。 - 增量
i++
发生 - 从上面的分配
i = 0
发生,有效地覆盖#2(后增量)会做的任何事情。
现在,#2实际上可能永远不会发生(可能不是?),因为编译器可能认识到它没有任何用处,但这可能是编译器的依赖。 无论哪种方式,其他更知识的答案已经表明,结果是正确的,符合C#标准,但没有定义在这里发生的C / C ++。
如何以及为什么会超出我的专业知识,但事实上,之前评估的右派分配发生在后增量可能是这里混淆。
此外,你不会指望结果是2,除非你确实i++
我相信++i
而不是i++
。
简单的说,
i ++,在“+ =”运算符完成之后,会将“i”加1。
你想要的是++ i,所以在“+ =”运算符被执行之前它会把“i”加1。
上面的答案有很多很好的推理,我只做了一个小testing,想和大家分享一下
int i = 0; i+ = i++;
这里结果我显示0结果。 现在考虑下面的情况:
情况1:
i = i++ + i; //Answer 1
以前我以为上面的代码看起来就像这样,所以起初看答案是1,而这个的真正答案是1。
案例2:
i = i + i++; //Answer 0 this resembles the question code.
这里增量运算符不会进入执行path,这与i ++在添加之前有机会执行的情况不同。
我希望这会有所帮助。 谢谢
i=0 i+=i i=i+1 i=0;
然后1被添加到i
。
I + = I ++
所以在给i
加上1之前, i
把0的值加了。只有在我们加1之前, i
才得到0的值。
i+=++i i=2
答案是i
将是1
。
让我们看看如何:
最初i=0;
。
然后在计算i +=i++;
根据价值我们会有像0 +=0++;
,所以根据运算符优先级0+=0
将先执行,结果为0
。
然后增量运算符将被应用为0++
,如0+1
, i
的值将为1
。