带后缀增量的三元运算符赋值

Ternary operator assignment with postfix increment

这有效,k 递增:

k = 0;
k = ( false condition here ) ? 0 : k+=1;

这有效,k 递增:

k = 0;
k = ( false condition here ) ? 0 : ++k;

这个不行,k总是0:

k = 0;
k = ( false condition here ) ? 0 : k++;

谁能解释一下幕后发生的事情?

编辑: 我不需要其他方式来写这个。 我不在乎这是否可以用更简单的方式编写。

在 for 循环中,i++ 或 ++i 都有效。 为什么这里的行为不同?

让我们让它更简单:

int x = 1;
x = x++;
// x still 1

为什么?

对于运算符优先级,首先执行++运算符,然后=赋值

让我们看看++。来自 https://msdn.microsoft.com/en-us/library/aa691363%28v=vs.71%29.aspx

If x is classified as a variable:

x is evaluated to produce the variable 

The value of x is saved. 
**1 is saved**

The selected operator is invoked with the saved value of x as its argument. 
**2 is produced**

The value returned by the operator is stored in the location given by the evaluation of x. 
**2 is put into x**

The saved value of x becomes the result of the operation.
**1 is returned**

现在我们有了作业:x = 1。所以 x 被分配 twice,一次是 2,一次是 1

i++++i 在 for 循环中的工作方式不同。

考虑以下 for 循环定义:

for (int i=0; i<1; ++i)

可以写成:

for (int i=0; 1 >= i ; ++i)

这相当于写作:

for (int i=0; 1 >= ++i; /*No function here that increments i*/)

在这种情况下,增量在比较之前执行,循环内容永远不会执行。

现在考虑这个循环:

for (int i=0; i<1; i++)

可以写成:

for (int i=0; 1 >= i ; i++)

这相当于写作:

for (int i=0; 1 >= i++; /*No function here that increments i*/)

这样的话,比较后执行自增,循环的内容会执行一次。在循环结束时,我的值为 1。

您可以通过执行以下操作进行测试:

int i;
for (i=0; i<1; i++)
{
  Console.WriteLine("i in the loop has a value of: " + i);
}
Console.WriteLine("i after the loop has a value of: " + i);

同样的规则也适用于三元运算符。

这种情况会在给k赋值之前执行增量,k会是26:

int k=25;
k= (false) ? 0 : ++k

而这种情况下k赋值后会执行increment,k为25:

int k=25;
k = (false) ? 0 : k++;

k+=1 与 ++ 运算符不同,k-=1 与 -- 运算符不同。 k+=1其实就是2个操作写的简洁:

k = k+1

它首先取k的值,在内存中对其加1,然后将值赋回给k。

所以在三元示例中:

int k=25;
k = (false) ? 0 : k+=1;

k 将为 26。赋值运算符从右到左求值。所以编译器会先计算k+=1 - 使k为26。然后执行三元比较,然后将26赋值给k。

总结:

++/-- 是特殊的运算符,你把它放在哪里会影响它在计算表达式时的执行时间。 ++x 或 --x - ++ 或 -- 将在任何比较之前进行评估。 x++ 或 x-- - ++ 或 -- 将在任何比较后进行评估

如果您想知道幕后发生了什么,我们可以查看 IL 级别。在此之前,我认为值得看看 xanatos 建议的 ++ 运算符的使用。

无论如何,让我们看一下第二种情况生成的IL。请看右边评论:

int k = 0; 
k =  false ? 0 : ++k;

IL_0001:  ldc.i4.0    // Allocate space for int k
IL_0002:  stloc.0     // assign 0 to k
IL_0003:  ldloc.0     // load k on top of the evaluation stack     --> our stack is [k]
IL_0004:  ldc.i4.1    // load value 1 at location 1 for variable k --> [k 1]
IL_0005:  add         // Pops and add the top two values on the evaluation stack, and push the result onto the stack. our stack is --> [1]
IL_0006:  dup         // Copies the current topmost value on the evaluation stack, and then pushes the copy onto the evaluation stack. which in our case is 1 --> [1 1]
IL_0007:  stloc.0     // Pop the top value on the stack at location 0 (e.g. assign it to k) --> [1]
IL_0008:  stloc.0     // same again, the stack is empty now --> []
IL_0009:  ret 

可以看到最后两个stloc.0把栈中的两个1赋给了k。事实上,如果你仔细想想,我们有两个任务。一个用于 ++k ,另一个用于分配三元运算的结果。正如你所说,这会产生 1。让我们看看你最后一个产生 0 的情况:

int k = 0; 
k =  false ? 0 : k++;

IL_0001:  ldc.i4.0    // Allocate space for int k
IL_0002:  stloc.0     // assign 0 to k
IL_0003:  ldloc.0     // load k on top of the evaluation stack     --> our stack is [k]
IL_0004:  dup         // Copies the current topmost value on the evaluation stack, and then pushes the copy onto the evaluation stack. which in our case is 1 --> [k k] in this case k is still 0!
IL_0005:  ldc.i4.1    // load value 1 at location 1 for variable k --> [k k 1]
IL_0006:  add         // Pops and add the top two values on the evaluation stack, and push the result onto the stack. our stack is --> [k 1] // because k+1 is equal 1
IL_0007:  stloc.0     // Pop the top value on the stack at location 0 (e.g. assign it to k) --> [1]
IL_0008:  stloc.0     // Pop the top value on the stack at location 0 (e.g. assign it to k) but in this case k is still zero!!!!! --> []

正如您通过 IL 中的注释看到的那样,两条 stloc.0 指令最终将 k 的原始值(即 0)分配给 k 本身。这就是为什么在这种情况下你得到 0 而不是 1 的原因。

我没有提供您问题的解决方案,只是解释了在 MSIL 中的以下级别如何处理这些 "simple" 操作。

希望对您有所帮助。

混合使用 post 增量和赋值时要小心。 pre 和 post 增量之间的区别出现在您将它们与作业一起使用时。

Post 递增 (++) 函数实现 returns 原始未触及的输入值(它保存临时副本)并递增输入:

i = 0
i = i++

post increment ++ 函数将作为输入 i=0 并将其递增。此时 i 实际上将是 1 但随后 post 增量函数 returns 原始值是 0 所以 i 将被重新分配最终值为 0

在经典的 for 循环 for(int i=0; i < val; i++ ) 中,使用 i++ 或 ++i 不会有什么不同,因为递增时不会发生赋值。这个 for 循环反而会让你头疼 for(int i=0; i < val; i=i++ )