带后缀增量的三元运算符赋值
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++ )
这有效,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++ )