对于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副本 – 每个操作一个。 (我将使用名称iAddiAssign分别用于+++=的临时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++;

评估顺序是:

  1. 存储我+我(即0 + 0)
  2. 递增我(我变成1)
  3. 将步骤1的值分配给i(i变为0)

并不是说增量正在被丢弃。

是什么意思: i = i++ + i;

这与前面的例子不一样。 第三i受到增量的影响。

评估顺序是:

  1. 存储我(即0)
  2. 递增我(我变成1)
  3. 步骤1 + i的存储值(即0 + 1)
  4. 将步骤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 ++现在被评估如下:

  1. 我现在的价值是多less? 它是0.存储它,所以我们可以添加i ++的结果。
  2. 评估我++(评估为0,因为这是我的当前值)
  3. 加载存储的值并将步骤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 。 这就是为什么你应该总是使用++ifor循环(虽然我认为所有现代编译器正在翻译i++++ifor循环)。

你的问题的唯一答案是正确的: 因为它是未定义的。

好的,在你们全部烧我之前

你们都回答为什么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=ii有同样的结果吗?

t=i++t=it=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之前,它会在我的值被评估之前发生,并给你预期的答案。

计算步骤是:

  1. int i=0 //初始化为0
  2. i+=i++ //等式
  3. i=i+i++ //在通过编译器简化公式之后
  4. i=0+i++我值替代
  5. i=0+0 // i ++是0
  6. 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度来回答这个问题。

在我看来,这是按顺序发生的:

  1. i被评估为0,导致i = 0 + 0 ,增量操作i++ “排队”,但是对于i的赋值还没有发生。
  2. 增量i++发生
  3. 从上面的分配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+1i的值将为1