或许大家都听说过,栈是先进后出,队列是先进先出,那CIL代码中入栈和出栈是怎么进行的呢?下面让我们一窥究竟吧!
先看一段简单的CIL代码:
.class public Program extends [mscorlib]System.Object
{
.method public static void Show()
{
.entrypoint
.maxstack 3
.locals init(
string info
)
ldstr "Name:{0} Age:{1}"
ldstr "Guo"
box string
ldc.i4 12
box int32
call string [mscorlib]System.String::Format(string,object,object)
stloc.0
ldloc.0
call void [mscorlib]System.Console::WriteLine(string)
call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
ret
}
}
上述CIL代码对应的C#代码如下:
public class Program
{
public static void Show()
{
string info = string.Foramt("Name:{0} Age:{1}","Guo",12);
Console.WriteLine(info);
Console.ReadKey();
}
}
下面开始分析入栈和出栈的顺序:
ldstr "Name:{0} Age:{1}"
ldstr "Guo"
box string
ldc.i4 12
box int32
执行完毕后堆栈中的内容从上到下依次是:12、”Guo”、”Name:{0} Age:{1}”,由于最先入栈的值会处于栈底位置,所以堆栈中的内容从上到下如图:
call string [mscorlib]System.String::Format(string,object,object)需要三个参数,所以要从堆栈中弹出三个值。此时12处于栈顶位置,所以首先被弹出,那12被弹出之后是放到string.Format()第1个参数的位置上吗?如果第1个弹出的值放到第1个参数的位置,第2个弹出的值放到第2个参数的位置,第3个弹出的值放到第3个参数的位置,那对应的C#代码应该是这样的:
string info = string.Foramt(12,"Guo","Name:{0} Age:{1}");
然而实际上生成的C#代码应该是这样的:
string info = string.Foramt("Name:{0} Age:{1}","Guo",12);
说明第1个弹出的值放到了最后1个参数的位置,第2个弹出的值放到了倒数第2个参数的位置,第3个弹出的值放到了第1个参数的位置。有一个简单的方法来快速判断方法各个参数对应的值是什么,CIL代码中各参数入栈的先后顺序是:”Name:{0} Age:{1}”、”Guo”、12,将堆栈中的值赋值给方法所需的各个参数后从第1个参数开始方法各个参数的值依次是:”Name:{0} Age:{1}”、”Guo”、12,说明第1个入栈的值就是方法第1个参数的值,第2个入栈的值就是方法第2个参数的值,以此类推。
这就是入栈和出栈的顺序,以及方法需要多个参数时,栈顶的值被弹出后该放到第几个参数的位置上,不知道各位看明白了吗?有意见请留言。