2014-11-02 95 views
2

当我试图了解回合实际上做我最后看一个简单的控制台程序(从C#.NET 4.5编译器发出)的IL:为什么编译器发出一个stloc后跟一个ldloca?

.maxstack 2 
.locals init (float64 V_0, 
     float64 V_1) 
IL_0000: ldc.r8  2.00
IL_0009: stloc.0 
IL_000a: ldloca.s V_0 
IL_000c: call  instance string [mscorlib]System.Double::ToString() 
IL_0011: call  void [mscorlib]System.Console::WriteLine(string) 
IL_0016: ldloc.0 
IL_0017: ldc.i4.5 
IL_0018: call  float64 [mscorlib]System.Math::Round(float64, 
                  int32) 
IL_001d: stloc.1 
IL_001e: ldloca.s V_1 
IL_0020: call  instance string [mscorlib]System.Double::ToString() 
IL_0025: call  void [mscorlib]System.Console::WriteLine(string) 
IL_002a: ret 

而且我注意到IL_001d的指示, IL_001e。它们对我来说似乎是多余的,将值存储在本地并在此之后加载相同的值。删除它们并重新组装IL给我一个NullReferenceException,所以我猜测它有一些指向。但我无法弄清楚。

那么NullReferenceException从哪里来?为什么编译器会发出这两条指令? 除了上面给出的IL之外,没有更多的IL,当然除了一些元。

回答

3

ldloca将变量的地址加载到堆栈上(而不是变量本身)。所以,这两个命令并不相反:stloc.1弹出变量值,由Math::Round返回,并且ldloca.s推动堆栈地址V_1,调用Double::ToString

+1

谢谢你的回答。根据你的指针进行更多的研究之后,我现在完全可以了解它。 '调用'操作码需要将参考推送到堆栈上,并且在堆栈上推入一个双精度值不能满足该要求。所以它需要存储在一个变量中,并且该变量的引用被压入堆栈,所以'call'很快乐。而且我认为发生了NullReferenceException,因为double值表示的地址没有任何内容。 – Vincent 2014-11-02 17:32:13

+0

我认为在这种情况下,异常类型取决于堆栈顶部的当前值:它可能是NullReferenceException中的任何内容,将垃圾打印到访问冲突。 – 2014-11-03 08:24:11

+1

关于'System.Double :: ToString'调用,这是实例方法,它期望堆栈中'Double'结构的地址。由于'double'变量具有与'Double'结构相同的内存布局,这使得'ToString'方法很开心。顺便说一句,这段代码表明,为'int,double'和类似类型调用'ToString'不会导致装箱。 – 2014-11-03 08:27:17

相关问题