2014-11-02 24 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

相關問題