下面的代码(可用作控制台应用程序):

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

i的结果是0。我以为会有2个(和我的一些同事一样)。可能编译器创建了某种结构,导致i为零。

我期望2的原因是,在我的思路中,右边的语句会先求值,使I加1。然后加上i,因为i已经是1了,所以是1加1。所以1 + 1 = 2。很明显,事实并非如此。

你能解释编译器做什么或者在运行时发生了什么吗?为什么结果是零?

某种免责声明:我非常清楚您不会(而且可能不应该)使用此代码。我知道我永远不会。尽管如此,我觉得了解它为什么会以这样的方式工作以及究竟发生了什么是很有趣的。


后修正自增操作符++,在表达式中给变量一个值,然后对i再次执行你分配的返回0(0)值的自增操作,这将覆盖增加的1(1),所以你得到0。您可以在++ operator (MSDN)中阅读更多关于自增操作符的信息。


因为i++首先返回值,然后对其递增。但是当i被设为1后,你把它设回0。


I += I ++;Will等于0,因为它在后面执行++。

I += ++ I;之前会做


这样的:

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++或x——的后缀递增或递减操作的运行时处理由以下步骤组成: 如果x被归为变量: 对X求值以生成变量。 保存x的值。 所选操作符将以保存的x值作为参数调用。 运算符返回的值存储在x求值所给出的位置中。 保存的x值成为操作的结果。

注意,由于优先级的顺序,后缀++出现在+=之前,但结果最终未被使用(因为使用了i的前一个值)。


更彻底地将i+ = i++分解为组成它的各个部分需要知道+=和++都不是原子操作(也就是说,它们都不是单个操作),即使它们看起来像是原子操作。这些实现的方式涉及到临时变量,在操作发生之前复制i -每个操作一个。(我将分别为++和+=使用的临时变量使用iAdd和iAssign的名称)。

所以,更接近实际情况的近似是:

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;

++后缀在对i加1之前求值,+=只求i一次。

因此,0 + 0 = 0,因为i是在加1之前计算和使用的,因为使用了++的后缀格式。要先增加i,请使用前缀形式(++i)。

(另外,只是注意:你应该只得到1,因为0 + (0 + 1)= 1)

参考资料:http://msdn.microsoft.com/en-us/library/sa7629ew.aspx (+=) http://msdn.microsoft.com/en-us/library/36x43w8w.aspx (+ +)


首先,i++返回0。然后i加1。最后,i被设置为i的初始值,即0加上i++返回的值,也为0。0 + 0 = 0。


变量后面的++操作符使其成为后加式。增量操作发生在语句中的其他所有操作之后,包括加法和赋值操作。相反,如果你把++放在变量之前,它会在i的值被计算之前发生,并给你预期的答案。


有两种选择:

第一个选项:如果编译器按如下方式读取语句,

i++;
i+=i;

那么结果是2。

For

else if
i+=0;
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++表达式,一次是+=语句。

但是+=语句的操作数是

在i++求值之前的值I(+=的左边)和 i++求值前的值I(+=的右边)。


后增量方法看起来像这样

int ++(ref int i)
{
    int c = i;
    i = i + 1;
    return c;
}

所以基本上当你调用i++时,i是增量,但在你的例子中,它返回的是0。


运行代码的反汇编:

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;

分解第二个代码(只是为了证明它们是相同的)

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反汇编窗口看到最终的内存汇编代码。它显示的是正在执行的机器代码,而不是CIL。

在调试时使用这个:

调试(菜单)-> Windows(子菜单)->拆卸

那么后缀++发生了什么?

后缀++表示我们想在求值…之后增加操作数的值。每个人都知道……让人有点困惑的是“评估之后”的含义。

那么“评估之后”是什么意思呢?

操作数在同一行代码上的其他用法必须受到影响: A = i++ + i,第二个i受增量影响 Func(i++, i)第二个i受到影响 在同一行中的其他用法是关于短路运算符的,如||和&&: (false && i++ != i) || i == 0第三个i不受i++的影响,因为它没有被计算

那么i+ = i++;是什么意思呢?

这和i = i+ i++;

求值顺序为:

存储i + i(即0 + 0) 增量i (i变成1) 将步骤1的值赋给i (i变为0)

并不是说增量被丢弃了。

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

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

求值顺序为:

存储i(那是0) 增量i (i变成1) 存储步骤1 + i的值(即0 + 1) 将第3步的值赋给i (i变成1)


这只是从左到右、自底向上的抽象语法树的计算。从概念上讲,表达式的树是从上到下遍历的,但是当递归从树的底部弹出时,计算就展开了。

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

计算从考虑根节点+=开始。这是这个表达式的主要组成部分。必须计算+=的左操作数,以确定存储变量的位置,并获得前值为零。接下来,右边必须求值。

右边是一个后递增的++操作符。它有一个操作数i,它既作为值的来源,也作为值的存储位置进行计算。运算符对i求值,找到0,然后将1存储到该位置。它根据返回先验值的语义返回先验值0。

现在控制回到+=运算符。现在它有了完成操作的所有信息。它知道存储结果的位置(i的存储位置)以及先前的值,并且它有要添加到先前值的值,即0。所以i最后是0。

像Java一样,c#通过固定求值顺序,清除了C语言中一个非常愚蠢的方面。从左到右,自底向上:程序员可能期望的最明显的顺序。


i++的意思是:返回i的值,然后增加它。

I += i++表示: 取i的当前值。 添加i++的结果。

现在,让我们加入i = 0作为起始条件。 I += i++现在是这样计算的:

i的当前值是多少?它是0。存储它,这样我们就可以将i++的结果添加到它。 Evaluate i++(求值为0,因为这是i的当前值) 加载存储值并将第2步的结果添加到其中。(0加0)

注意:在第2步结束时,i的值实际上是1。但是,在第3步中,通过在i的值增加之前加载它来丢弃它。

与i++不同,++i返回的是增加后的值。

因此,i+= ++i会得到1。


简单的答案

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是1。

让我们来看看是如何做到的:

起初我= 0;。

然后在计算i+ =i++;根据的值,我们会得到类似0+=0 ++;,所以根据运算符优先级,0+=0将首先执行,结果将是0。

然后增量操作符将应用为0++,即0+1,i的值将为1。


关于这一点,我一直记得一个很好的助记法:

如果++位于表达式之后,则返回之前的值。下面的代码

int a = 1;
int b = a++;

是1,因为a在被a后面的++加起来之前是1,人们称之为后缀符号。还有一种前缀表示法,情况正好相反:如果++位于前面,表达式将返回操作之后的值:

int a = 1;
int b = ++a;

这里B是2。

对于你的代码,这意味着

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。这就是为什么你应该总是在for循环中使用++i(尽管我认为所有现代编译器都在for循环中将i++转换为++i)。


要非常小心:阅读C常见问题:你试图做的事情(同一个变量的赋值和++混合)不仅没有指定,而且也是未定义的(这意味着编译器在求值时可能会做任何事情!),不仅给出“合理”的结果)。

请阅读第三部分。整个章节都很值得一读!特别是3.9,这解释了未指明的含义。3.3节简要介绍了使用“i++”之类的工具可以做什么,不能做什么。

根据编译器内部的不同,您可能会得到0、2、1,甚至其他任何值!因为它是没有定义的,所以他们这样做是可以的。


计算步骤如下:

int i=0 //初始化为0 我+ + + = / /方程 I = I +i++ //通过编译器简化后的方程 I =0+i++ // I值替换 I =0+0 //i++是0,如下所述 i=0 //最终结果i=0

这里,i的初始值是0。 WKT, i++只是:首先使用i值,然后将i值增加1。所以 它在计算i++时使用I值0,然后将其加1。 所以结果是0。


i=0

i+=i

i=i+1

i=0;

然后把1加到i上。

i+=i++

在给i加1之前,i取了0。只有在前面加1,我才得到0。

i+=++i

i=2

c#在做什么,以及困惑的“为什么”

我还期望值为1…但对这个问题的一些探索确实澄清了一些观点。

考虑以下方法:

    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))相同。语句之后的i值是1:

int i = 0;
SetSum(ref i, Inc(ref i));
Console.WriteLine(i); // i is 1

但后来我得出了另一个结论……I += i++实际上和I = I + i++…所以我创建了另一个类似的例子,使用这些函数:

    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#在做什么……但这也是为什么很多人对它感到困惑……包括我。


希望从C编程101的角度来回答这个问题。

在我看来,它是这样发生的:

I被求值为0,导致I = 0 + 0,使用递增操作i++ "queued",但将0赋值给I也没有发生。 发生增量i++ 上面的赋值i = 0发生了,有效地覆盖了#2(后增量)所做的任何事情。

现在,#2可能永远不会真正发生(可能不会?),因为编译器可能意识到它没有任何作用,但这可能取决于编译器。不管怎样,其他更有知识的回答已经表明结果是正确的,并且符合c#标准,但是这里没有定义C/ c++的情况。

如何和为什么超出了我的专业知识,但事实是,前面计算的右边赋值发生在后增量之后,这可能是令人困惑的地方。

此外,你不会期望结果是2,除非你用++i而不是i++,我相信。


简单地说,

i++,将在“+=”运算符完成后给“I”加1。

你想要的是++i,这样它就会在“+=”运算符执行之前给“i”加1。


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

我+ = + +;结果0是未定义的。

如果你愿意的话,语言评估机制中的一个bug。甚至更糟!设计中的bug。

想要证明吗?你当然想!

int t = 0;int i = 0;t + = + +;/ / t = 0;i = 1

现在这…是直观的结果!因为我们首先对t求值并赋值只有在求值和赋值之后我们才进行后期操作,不是很合理吗?

i=i++和i=i对i产生相同的结果是合理的吗?

而t=i++和t=i对于i有不同的结果。

post操作应该发生在语句求值之后。 因此:

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的结果都表明编译器中的错误或语言设计中的错误-然而MSDN和许多其他来源告诉我们“嘿,这是未定义的!”

现在,在我继续之前,即使我给出的这组例子也没有得到任何人的支持或承认。然而,这是根据直觉和理性的方式应该得到的结果。

编码器不应该知道程序集是如何编写或翻译的!

如果它以一种不尊重语言定义的方式编写——这是一个bug!

And to finish I copied this from Wikipedia, Increment and decrement operators : Since the increment/decrement operator modifies its operand, use of such an operand more than once within the same expression can produce undefined results. For example, in expressions such as x − ++x, it is not clear in what sequence the subtraction and increment operators should be performed. Situations like this are made even worse when optimizations are applied by the compiler, which could result in the order of execution of the operations to be different than what the programmer intended.

因此。

正确的答案是不应该使用!(因为它没有定义!)

是的. .-它有不可预测的结果,即使c#编译器试图以某种方式规范化它。

我没有找到任何关于c#的文档,把你们所有人都记录下来的行为描述为一种正常的或定义良好的语言行为。但我的发现恰恰相反!

[从MSDN文档中复制后缀自增和自减操作符:++和——]

当后缀操作符应用于函数实参时,不保证实参的值在传递给函数之前是递增或递减的。有关更多信息,请参阅c++标准中的1.9.17节。

注意这些话并不能保证……


上面的答案有很多很好的推理,我只是做了一个小测试,想和你分享

int i = 0;
i+ = i++;

这里result i显示0 result。 现在考虑以下情况:

案例1:

i = i++ + i; //Answer 1

之前我认为上面的代码类似于这个,所以第一眼看答案是1,而这个I的真正答案是1。

案例2:

i = i + i++; //Answer 0 this resembles the question code.

这里的增量操作符没有出现在执行路径中,不像前面的情况,i++有机会在加法之前执行。

我希望这对你有所帮助。谢谢