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

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。很明显,事实并非如此。

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

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


当前回答

运行代码的反汇编:

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)

其他回答

i=0

i+=i

i=i+1

i=0;

然后把1加到i上。

i+=i++

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

i+=++i

i=2

++后缀在对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 (+ +)

这样的:

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;

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++,我相信。