【问题标题】:What exactly does the == operator do?== 运算符到底是做什么的?
【发布时间】:2016-02-14 19:55:56
【问题描述】:

所以我查看了 ILDASM,检查了一个如下所示的 .exe:

int a = 2;
Int32 b = 1;
if(b == 1)
{

}

现在,CIL 代码如下所示:

IL_0005:  ldloc.1
IL_0006:  ldc.i4.1
IL_0007:  ceq
IL_0009:  ldc.i4.0
IL_000a:  ceq
IL_000c:  stloc.2

我知道首先加载 b(存储在 [1] 中),然后加载一个值为 1 的常量,然后将它们进行比较。我不明白为什么在存储比较结果之前加载和比较另一个值为 0 的常量。
由于第一次比较应该已经产生了一个真值,所以检查这个值是否为 0 会反转结果,对吧?
我现在的问题是:为什么它是倒置的? 我认为它与我使用的 == 运算符有关,我的理论是它返回差异。如果此差异为 0,则值相同,因此结果应为 true。但是0代表假,所以需要取反。
我似乎找不到任何关于这个话题的东西,只是关于像 ==~ 这样的运算符的东西。 希望大家多多指教:)

最好的问候

威尔苏

PS:这是完整的代码:

.method private hidebysig instance void  Form1_Load(object sender,
                                                class [mscorlib]
System.EventArgs e) cil managed
{
// Code size       19 (0x13)

.maxstack  2
.locals init ([0] int32 a,
       [1] int32 b,
       [2] bool CS$4$0000)
IL_0000:  nop
IL_0001:  ldc.i4.2
IL_0002:  stloc.0
IL_0003:  ldc.i4.1
IL_0004:  stloc.1
IL_0005:  ldloc.1
IL_0006:  ldc.i4.1
IL_0007:  ceq
IL_0009:  ldc.i4.0
IL_000a:  ceq
IL_000c:  stloc.2
IL_000d:  ldloc.2
IL_000e:  brtrue.s   IL_0012
IL_0010:  nop
IL_0011:  nop
IL_0012:  ret
} // end of method Form1::Form1_Load

【问题讨论】:

  • 我根本没有得到那个 IL。我的猜测是它来自另一段代码。请发布一个简短但完整的示例。
  • 我得到了相同的(至少相似的结果)只有这个代码在 main.
  • 你正在编译发布吗? (我没有看到任何“nop”,但是...),并且:这是整个代码吗?看起来它做了一些有趣的重新排序;p 但是:我没有看到第三个变量,所以对我来说 stloc.2 甚至 意味着... 并不明显...跨度>
  • 我非常确信您正在查看调试版本;简短的回答是:它做了很多不必要的事情使调试更容易。您不应该过多关注调试版本中的 IL。
  • 这不是完整的代码。我从 IL_0005 开始,上面有 4 行,还有 .maxstack 2 和 .locals init。其余的代码太长了,无法评论……我会把它添加到我的问题中。

标签: c# operators cil comparison-operators


【解决方案1】:

据我所知,它正在跳转到函数的末尾。

void Main()
{
    int a = 2;
    Int32 b = 1;
    if(b == 1)
    {
        Console.WriteLine("A");
    }
}

给我:

IL_0000:  nop         
IL_0001:  ldc.i4.2    
IL_0002:  stloc.0     // a
IL_0003:  ldc.i4.1    
IL_0004:  stloc.1     // b
IL_0005:  ldloc.1     // b
IL_0006:  ldc.i4.1    
IL_0007:  ceq         
IL_0009:  ldc.i4.0    
IL_000A:  ceq         
IL_000C:  stloc.2     // CS$4$0000
IL_000D:  ldloc.2     // CS$4$0000
IL_000E:  brtrue.s    IL_001D
IL_0010:  nop         
IL_0011:  ldstr       "A"
IL_0016:  call        System.Console.WriteLine
IL_001B:  nop         
IL_001C:  nop         
IL_001D:  ret         

从 IL_0005 开始,我们有:

加载b.
加载1.
ceq(如果相等,推1,如果为假推0) - 这里的结果将是1
加载0
ceq - 这里的结果将是0
brtrue.s IL_001D - 如果值非零,跳转到IL_001D(函数结束)

所以它本质上是这样编译的:

int a = 2;
Int32 b = 1;
if(!(b == 1))
    goto end;
Console.WriteLine("A");
:end
return;

【讨论】:

  • 对于上下文,当我编译这个(优化)时,我得到:ldc.i4.1ldc.i4.1bne.un.s {to the ret}ldstr "A"call void [mscorlib]System.Console::WriteLine(string)ret - 非常不同
  • 虽然坦率地说我有点失望,因为它没有完全删除它并编译为ret,因为在运行时比较两个常量似乎......愚蠢
  • 非常感谢,这正是我想知道的。
【解决方案2】:

ceq 从堆栈中获取两个值,如果它们被认为相等,则返回 1,否则返回 0。但是,C#中的==是否会导致ceq取决于很多事情:

  • 数据类型
    • 它们是原语吗?
    • 他们有自定义的== 运算符吗?
    • 它们是参考文献吗?
  • 上下文
    • 可以优化成其他东西吗? (我在类似的例子中得到了bne.un.s;还有beq*br*switch等)
    • 可以完全删除吗?

【讨论】:

  • 关于数据类型:这就是我使用整数的原因。原始数据类型,没有重载的运算符。我从来没有想过像 == 运算符这样简单的东西可以进一步优化......你让我感兴趣,我会看看 bne.un.s 现在是什么。感谢您的宝贵时间。
猜你喜欢
  • 1970-01-01
  • 2019-06-10
  • 2015-03-28
  • 2021-07-15
  • 1970-01-01
  • 2014-09-16
  • 2013-08-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多