還是學習筆記1中的那個例子,可以改成如下的樣子,功能不變,但運行時佔用的資源會小一些。我在所有和筆記1中代碼有了變化的位置加上了注釋,如下: 1//----------- Program header
2.assembly extern mscorlib { }
3.assembly OddOrEven { }
4.module OddOrEven.exe
5//----------- Class declaration
6.namespace Odd.or {
7 .class public auto ansi Even extends [mscorlib]System.Object {
8//----------- Field declaration
9 .field public static int32 val
10//----------- Method declaration
11 .method public static void check( ) cil managed {
12 .entrypoint
13 .locals init (int32 Retval)
14 AskForNumber:
15 ldstr "Enter a number"
16 call void [mscorlib]System.Console::WriteLine(string)
17 call string [mscorlib]System.Console::ReadLine()
18 ldstr "%d" // 所有的字串常量都不需要特殊的聲明,il彙編器看到這樣的字串之後會自動將其加入到中繼資料中
19 ldsflda int32 Odd.or.Even::val
20 call vararg int32 sscanf(string,string,,int32*)
21 stloc.0 // java的虛機和.net clr都使用0,1,2,3這樣的編號來表示本地變數。這裡的"0"就指向唯一的一個本地變數Retval
22 ldloc.0 // 將第一個本地變數的值壓棧
23 brfalse.s Error // brfalse.s指令是brfalse的簡化指令。當指令被編譯成作業碼之後,brfalse接受的是一個4位元組的參數,
24 //而brfalse.s僅接受一個單位元組的參數,這意味著只能在程式碼片段的{-128位元組~~+128位元組}的範圍內(相對於當前位置而言)跳轉。
25 ldsfld int32 Odd.or.Even::val
26 ldc.i4.1 // 向棧頂壓入一個單位元組常量,值為1(ldc.i4 1也是向棧頂壓入1,但要用4個位元組)
27 and
28 brfalse.s ItsEven
29 ldstr "odd!"
30 br.s PrintAndReturn
31 ItsEven:
32 ldstr "even!"
33 br.s PrintAndReturn
34 Error:
35 ldstr "How rude!"
36 PrintAndReturn:
37 call void [mscorlib]System.Console::WriteLine(string)
38 ldloc.0
39 brtrue.s AskForNumber
40 ret
41 } // End of method
42 } // End of class
43} // End of namespace
44//----------- Calling unmanaged code
45.method public static pinvokeimpl("msvcrt.dll" cdecl)
46 vararg int32 sscanf(string,string) cil managed { }
47
另外,提起上文中的ldloc.0這樣的指令,筆者一開始也是未明其意。其實,在clr執行程式的過程中,涉及到的記憶體使用量主要有三類,除了我們常說的託管堆,每個線程都有自己專屬的兩個棧:計算棧(Evaluation Stack)和調用棧(Call Stack)。這兩個棧都經常被提到,又都專屬於clr線程,所以有的時候會混,其實它們的功能是完全不同的。調用棧就是最傳統的用來記錄方法調用次序的棧,每個線程會得到1MB的調用棧空間,每調用一個新的方法都會壓棧,傳入參數、臨時變數等都會壓入棧中,待方法執行完畢就完全釋放。ldloc.0中的"0",所指的就是當前調用棧上的第一個臨時變數。而計算棧在某種程度上可以視為clr虛擬機器的寄存器(這麼說只是因為它是和虛機上的計算功能直接互動的儲存單位),我們常說的clr虛機是完全基於“棧”的,指的就是計算棧。虛機上的所有操作,都要從計算棧的參數,結果也都會壓入到計算棧頂。計算棧的單位既不是字,也不是位元組,而是slot(怎麼翻譯合適呢,咳咳),根據slot內所裝資料類型的不同,slot的大小也不同。當clr棧頂的slot裡取到資料之後,在運算之前會檢驗資料的類型,如果無法轉換成運算期望的類型,會拋出異常。蔡學庸在msdn上有篇文章,裡面的一個貼圖看著很明白,就在這裡借用一下吧:
參考資料:MSIL instruction table