C#代码
class Program { static void Main(string[] args) { string b = GetAge(); Console.ReadKey(); } private static string GetAge() { var a = "a"; var b = "a"; var c = a; return c; } }
使用Reflector 反编译 IL代码
.class private auto ansi beforefieldinit Program extends [mscorlib]System.Object { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: call instance void [mscorlib]System.Object::.ctor() L_0006: ret } .method private hidebysig static string GetAge() cil managed { .maxstack 1 .locals init ( [0] string str, [1] string str2, [2] string str3, [3] string str4) L_0000: nop L_0001: ldstr "a" L_0006: stloc.0 L_0007: ldstr "a" L_000c: stloc.1 L_000d: ldloc.0 L_000e: stloc.2 L_000f: ldloc.2 L_0010: stloc.3 L_0011: br.s L_0013 L_0013: ldloc.3 L_0014: ret } .method private hidebysig static void Main(string[] args) cil managed { .entrypoint .maxstack 1 .locals init ( [0] string str) L_0000: nop L_0001: call string ILSample.Program::GetAge() L_0006: stloc.0 L_0007: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() L_000c: pop L_000d: ret } }
关键字解释
1. .class中 auto 关键字什么意思?
《StructLayout特性》《.net托管环境下struct实例字段的内存布局(Layout)和大小(Size)》
2. .class中 beforefieldinit 关键字什么意思?
这个关键字表示让运行时决定什么时候调用静态构造函数,也可能在调用函数的开始处,也可能恰恰在第一次访问静态成员或者第一次实例化之前。
总之是运行时决定的,只要在你用到这个类之前搞定就好了
如果没有这个关键字,则运行时恰恰在第一次访问静态成员或者第一次实例化之前调用静态构造函数。
至于C#编译的IL什么情况下有这个关键字什么时候没有关键字,判断起来也很容易,如果有显式的静态构造函数,就没有这个关键字,如果没有显式的构造函数,就有这个关键字。
关于beforefieldinit还有很多有意思的内容,可以参考下面这些园子里高手的文章
《关于Type Initializer和 BeforeFieldInit的问题,看看大家能否给出正确的解释》
《[你必须知道的.NET]第二十三回:品味细节,深入.NET的类型构造器》
3. .method中 .entrypoint 关键字什么意思?
.entrypoint这个比较简单,就是程序的入口点,如果是.EXE的程序集有且只能有一个,如果是.DLL的程序集,当然可以没有入口点。
C#的Main()函数,就被编译器加上了.entrypoint,IL编译器是不认Main()的,只认.entrypoint,所以在IL中,你可以随便让一个函数当作入口。没必要非是Main()。
4. .method中 hidebysig 关键字什么意思?
基本相当于C#中的new关键字
5. 每个指令前边的 IL_00XX: 是神马东西?
IL_XXXX:其实就是语句的标号,这里的这些标号是ILDASM生成的,就程序执行来说并没有意义。
我们也可以根据自己的情况来写标号,比如"BEGIN: END:"等等
也可以通过标号让程序来跳转,C#不也是有这个功能的么?
接下来的问题就是IL_XXXX后便XXXX这几个数字了,是按什么编号的?其实很明显感觉是相对于方法入口的地址偏移。
为了证实这一点,我也没想到什么好办法,随便写了一个C#程序,反编译后为
IL_0000: ldstr "dddddddddddddddd" //72
IL_0005: call void [mscorlib]System.Console::WriteLine(string) //28
IL_000a: ldstr "ffffffffffffffff" //72
IL_000f: call void [mscorlib]System.Console::WriteLine(string) //28
IL_0014: ldstr "dddddddddddddddd" //72
IL_0019: call void [mscorlib]System.Console::WriteLine(string) //28
IL_001e: ldc.i4.3 //19
IL_001f: stloc.0 //0A
IL_0020: ldc.i4.4 //1A
IL_0021: stloc.1 //0B
IL_0022: ldloc.0 //06
IL_0023: ldloc.1 //07
IL_0024: add //58
IL_0025: stloc.2 //0C
IL_0026: ldloc.2 //08
IL_0027: call void [mscorlib]System.Console::WriteLine(int32) //28
IL_002c: ret //2A
用UltraEdit打开了.EXE文件。
找到这段IL代码表示二进制代码(OpCode),查找的方法当然要找到每条指令对应的OpCode,
比如0x72代表ldstr;0x28代表call;0x19代表ldc.i4.3 更多需要参考《MSIL指令操作Opcode对照表》
按照内存的顺序将列下来,就很容易看出来代码地址偏移的关系。
版权声明:本文为博主原创文章,未经博主允许不得转载。