MSIL 初级读本 第二部分:局部变量

来源:互联网 发布:seo编辑工作内容 编辑:程序博客网 时间:2024/04/29 19:35

 

声明:    本文译自 Kenny Kerr  的 blog,如果大家有更好的 MSIL 入门文章也欢迎推荐,谢谢。



    在本文的第二部分里,我将探讨一下局部变量的使用。为了展示这个过程,让我们来写一个将两数相加的简单程序。
    在 MSIL 方法中,使用 .locals 指示符来声明局部变量

    

.local init( int32 first, 
                  int32 second,
                  int32 result )


   
    该语句为当前方法声明了三个局部变量。在本例中,恰巧都是 int32 类型—— System.Int32 类型的同义词。init 指定这些变量需要以其对应类型的默认值进行初始化。变量名称也可以被省略,那样的话你需要通过声明时的zero-based 索引来指明变量。当然,使用变量名会增加代码的可读性。
    在我们继续之前,需要先明确一下 MSIL 当中显式使用栈的方式。当你需要向一个指令传值的时候,首先需要把那些值压栈。而指令需要进行弹栈操作以读出数值。类似地,在调用方法时也需要将对象引用 ( 如果有的话 ) 和需要传递的参数按顺序压栈。在开始调用方法时,所有的参数以及对象引用会被弹栈。使用 ldloc 指令来将变量的值压栈;使用 stloc 指令将栈顶的值弹出并保存到指定变量中。另外要时刻记得,值类型的右值会被直接存储到栈中,而对象 ( 引用类型的实例 ) 不会,因为 CLI 不允许在栈上为引用类型的对象分配内存,而只是将对象的引用存储到栈上。这类似于将一个原生C++对象分配在堆上并将一个指向它的指针存储在栈中。请在阅读本文的时候记住这个栈的存在,这将有助于理解为什么数值会不停地在栈上压入弹出。 
    下一步是要让用户输入相加的数值。

    

ldstr "First number: "
call void [mscorlib]System.Console::Write(string)
call string [mscorlib]System.Console::ReadLine()
call int32 [mscorlib]System.Int32::Parse(string)
stloc first


    
    上一篇文章中提到, ldstr 指令将字符串压栈,而 call 指令调用 Write 方法,并将其参数弹栈。下一个 call 指令调用 ReadLine 方法,该方法从控制台读入并返回的字符串被压栈。因为返回值正好位于栈顶,所以我们直接调用 Int32::Parse 方法,将读入的字符串弹栈并将其等价的 int32 类型的数值压栈。注意为了清楚起见,我省略了所有的错误处理。接下来用 stloc 指令将数值弹栈并存储于局部变量 first 当中。第二个数值以同样的方式获得,并存储于变量 second 当中。
    接下来,我们使用 add 指令完成加法并将结果保存到变量 result 。 

    

ldloc first
ldloc second
add
stloc result


    
    最后显示结果。

   

ldstr "{0} + {1} = {2}"
ldloc first
box int32
ldloc second
box int32
ldloc result
box int32
call void [mscorlib]System.Console::WriteLine(string, object, object, object)



    我们使用WriteLine 的一个重载版本,它接收一个格式化字符串和三个 object 对象作为参数。调用之前,每一个参数都必须按顺序压栈。因为数值是以 int32 这个值类型来存储的,所以我们需要对其进行装箱操作,否则将无法匹配方法签名。 ldloc 指令将每一个参数压栈,随后 box 指令被应用于每个参数。装箱操作将数值弹栈,而后构造一个包含该数值拷贝的新对象,并将新对象的引用压栈。
    完整地程序如下:

.method static void main()
{
    .entrypoint
    .maxstack 4

    .locals init (int32 first,
                 int32 second,
                 int32 result)
    ldstr "First number: "
    call void [mscorlib]System.Console::Write(string)
    call string [mscorlib]System.Console::ReadLine()
    call int32 [mscorlib]System.Int32::Parse(string)
    stloc first

    ldstr "Second number: "
    call void [mscorlib]System.Console::Write(string)
    call string [mscorlib]System.Console::ReadLine()
    call int32 [mscorlib]System.Int32::Parse(string)
    stloc second
    ldloc first
    ldloc second
    add
    stloc result
    ldstr "{0} + {1} = {2}"
 
    ldloc first
    box int32

    ldloc second
    box int32

    ldloc result
    box int32
    
    call void [mscorlib]System.Console::WriteLine(string, object, object, object)??? 
    ret
}
     
    最后值得注意的是,本例中的程序需要大小为4的栈空间,因为最后调用 WriteLine 方法时需要传入4个参数。 
 

原创粉丝点击