前几天群里有位水友提问:”C#中,当一个方法所传入的参数是一个静态字段的时候,程序是直接到静态字段拿数据仍是从复制的函数栈中拿数据“。其实很明显,这和方法参数的传递方式有关,若是是引用传递的话,确定是会去静态字段直接拿值的;若是方法是以传值的方式使用参数的话,必定是从复制的栈中拿值的。html
可是这位水友就是不相信这个结论(后来发现,这货整一个杠精啊!每天在群里跟人抬杠~),这个时候我突然想到了能够反向看一下C#的IL代码,来了解一下程序运行的过程。的确,当咱们对运行结果有异议的时候,能够经过IL代码透过表面看本质。数组
在咱们分析查看IL以前首先要了解下什么是IL?IL的全称是Intermediate Language (IL)即将.NET代码转化为机器语言的一个中间语言的缩写。在必定程度上,咱们能够将其理解为伪汇编语言。咱们在使用.NET框架中的C#、VB.NET、F#等语言的时候,编译过程并非像C/C++同样直接编译出原生代码,而是编译成IL中间语言。经过IL中间语言这种方式,能够实现跨平台、提升程序灵活性等多种优势。缓存
下面咱们以C#语言为例,大体了解了解一下咱们的源代码是如何编译成IL语言,继而运行在电脑上面的。性能优化
图1:.NET语言编译过程示意图框架
上图为C#语言的编译运行过程示意图。首先编译器将咱们编写好的源代码编译成IL中间语言,这些IL中间语言的主要内容是一些元数据和中间语言指令。而后再由咱们的JIT编译器加载这些IL中间语言,JIT编译器会根据系统环境将IL中间语言指令转换为机器码,继而运行在不一样的目标平台上,实现跨平台功能。(JIT编译器将IL中间语言即时编译成原生语言的过程和解释性语言的读取一条执行一条又有些不一样,JIT会对编译结果进行缓存以便下次调取的时候直接使用)这也是为何有些ASP.NET网站第一次运行时会较慢,然后面的执行速度则会相对快不少的一个缘由。函数
再总结一下上面所说的编译过程:工具
在上文中咱们提到了一个JIT编译器,它的全名叫即时编译器。顾名思义,它是在运行时环境中发生的编译行为。读到这里,相信不少朋友可能都会像马三同样产生疑问了。相比传统的直接将源代码编译成原生代码,C#将源代码编译成了中间语言不会下降效率嘛?原来直接一步到位的过程,如今偏要拆成两个部分。这不只要花费更多的时间、占用更多的内存,还有可能下降性能,那用JIT编译器的好处到底有什么呢?性能
其实,使用JIT编译器的好处多多。若是咱们不用JIT即时编译将编译后的程序放在运行时中的话,那就只能生成在在某一种CPU平台上运行的原生代码,若是程序要运行在多种目标平台上的话,就要编译多种目标平台的原生代码,这样C#也就失去了跨平台能力。其次,JIT即时编译生成原生代码发生在运行时阶段,所以即时编译器会对生成的原生代码进行性能优化。通过优化的原生代码要比不优化的代码性能好。测试
在咱们的Unity游戏开发中就存在着AOT编译和JIT编译两种编译方式,之后咱们会单独开篇博客来详细探讨一下这两种编译方式的异同,这里就再也不赘述了。优化
上面说了一大堆概念和理论,相信你们早已经技痒,别急,下面,马三就和你们一块儿使用ILDasm工具反编译并查看IL代码。ILDasm工具通常在咱们安装Visual Studio的时候就已经默认安装好了,查看IL代码一般只需下面的几步操做:
1.首先,须要打开ILDasm工具,点击电脑桌面的开始,而后在程序中找到对应版本的Visual Studio的目录,接着在里面找到VS开发人员命令提示,英文版的名称是:Developer Command Prompt 。点击打开它,而后在命令行里面输入ILDasm并回车,就会打开ILDasm工具了。
图2:VS开发人员命令提示示意图
2.而后,在ILDasm工具的界面,点击文件-->打开,而后选择咱们预先编译出来的.exe文件,ILDasm工具就会自动的帮咱们分析出IL代码及其代码组织结构,以下图所示:
图3:解析出来的IL代码及相关代码组织结构
解析出来的IL代码,会以一些小图标区分标识出函数、接口等不一样的部分,ILDasm中图标含义以下图所示:
图4:ILDasm中图标含义
经过上面的两步,咱们就能够轻松的反编译出程序的IL代码,下面咱们经过一个小案例,来解读一下IL代码的运行流程以及分析IL代码的方式。
好了,如今让咱们回到博客最初抛出的那个问题上面来:“C#中,当一个方法所传入的参数是一个静态字段的时候,程序是直接到静态字段拿数据仍是从复制的函数栈中拿数据?”下面咱们就以这个问题为例子,经过反编译出IL代码,简单地验证一下。
首先贴出咱们的C#代码,很简单,在两个方法中分别以传值和传递引用的方式传入同一个静态变量:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace ILDemo 8 { 9 10 class Program 11 { 12 public static int n = 0; 13 static void Main(string[] args) 14 { 15 Func1(n); 16 Console.WriteLine(n); 17 Func2(ref n); 18 Console.WriteLine(n); 19 } 20 21 public static void Func1(int n) 22 { 23 n += 5; 24 } 25 26 public static void Func2(ref int n) 27 { 28 n += 5; 29 } 30 } 31 32 }
而后咱们对上面的代码进行编译,以后再按照第三节的操做,将其反编译成IL代码,如下是反编译出来的IL代码结构:
图5:反编译IL代码结构
首先让咱们观察一下Func1反编译出来的IL代码,以下图所示:
图6:Func1反编译出来的IL代码
马三简单地给你们分析一下上面的IL代码的意思:
nop:没有什么意义;
ldarg.0:将索引为 0 的参数加载到计算堆栈上,也就是把参数 n 放到堆栈上;
ldc.i4.5:将整数值 5 做为 int32 推送到计算堆栈上,由于咱们在程序中执行了+5的操做(上面的C#代码),因此须要先把5推送到堆栈上,以便下一步进行add操做;
add:将两个值相加并将结果推送到计算堆栈上,这里是将 n和5 相加,而后把结果推送到堆栈上;
Starg.S n:将位于计算堆栈顶部的值存储在参数槽中的指定索引处,即将n进行存储
ret:从当前方法返回,并将返回值(若是存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。
接着,咱们再来看看Func2反编译出来的IL代码:
图7:Func2反编译出来的IL代码
能够看到,由于咱们的C#代码中使用了ref参数,因此在IL代码中将其翻译成了int32& n的形式,和C++是否是很相似?
nop:没有什么意义;
ldarg.0:将索引为 0 的参数加载到计算堆栈上,也就是把参数 n的地址放到堆栈上;
ldind.i4:将 int32 类型的值做为 int32 间接加载到计算堆栈上
ldc.i4.5:将整数值 5 做为 int32 推送到计算堆栈上,由于咱们在程序中执行了+5的操做(上面的C#代码),因此须要先把5推送到堆栈上,以便下一步进行add操做;
add:将两个值相加并将结果推送到计算堆栈上,这里是将 n和5 相加,而后把结果推送到堆栈上;
stind.i4:在所提供的地址存储 int32 类型的值,即把咱们计算出来的结果,再存储回静态变量n的地址里面;
ret:从当前方法返回,并将返回值(若是存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。
经过比较,咱们能够很明显地发现因为参数传递的方式不一样,Func1和Func2调用的IL指令也不近相同,最后让咱们再看一下Main函数的执行过程再作分析。
图8:Main函数反编译出来的IL代码
反编译出来的Main函数IL指令执行流程以下:
nop:没有什么意义;
Ldsfld:将静态字段的值推送到计算堆栈上,这里就是把n的值推送到计算堆栈上;
call:调用由传递的方法说明符指示的方法,也就是调用了Func1函数;
nop:没有什么意义;
Ldsfld:将静态字段的值推送到计算堆栈上,这里就是把n的值推送到计算堆栈上;
call:调用由传递的方法说明符指示的方法,调用打印方法,将n的值输出到控制台
nop:没有什么意义;
Ldsflda:将静态字段的地址推送到计算堆栈上,就是把静态变量n在内存中的地址推送到计算堆栈上;
call:调用由传递的方法说明符指示的方法,也就是调用了Func2函数;
nop:没有什么意义;
Ldsfld:将静态字段的值推送到计算堆栈上,这里就是把n的值推送到计算堆栈上;
call:调用由传递的方法说明符指示的方法,调用打印方法,将n的值输出到控制台
通过上面的一系列漫长地分析,咱们能够得出结论:C#中,当一个方法所传入的参数是一个静态字段的时候,若是是引用传递的话,确定是会去静态字段直接拿值的;若是方法是以传值的方式使用参数的话,必定是从复制的栈中拿值的。“”
为了方便你们对照查看IL代码,理解IL指令的意义,马三在这里给你们提供了一个详细的IL指令对照表(Zery提供),原版的IL指令对照表你们能够到MSDN上面查看,附上连接。
名称 | 说明 |
Add | 将两个值相加并将结果推送到计算堆栈上。 |
Add.Ovf | 将两个整数相加,执行溢出检查,而且将结果推送到计算堆栈上。 |
Add.Ovf.Un | 将两个无符号整数值相加,执行溢出检查,而且将结果推送到计算堆栈上。 |
And | 计算两个值的按位“与”并将结果推送到计算堆栈上。 |
Arglist | 返回指向当前方法的参数列表的非托管指针。 |
Beq | 若是两个值相等,则将控制转移到目标指令。 |
Beq.S | 若是两个值相等,则将控制转移到目标指令(短格式)。 |
Bge | 若是第一个值大于或等于第二个值,则将控制转移到目标指令。 |
Bge.S | 若是第一个值大于或等于第二个值,则将控制转移到目标指令(短格式)。 |
Bge.Un | 当比较无符号整数值或不可排序的浮点型值时,若是第一个值大于第二个值,则将控制转移到目标指令。 |
Bge.Un.S | 当比较无符号整数值或不可排序的浮点型值时,若是第一个值大于第二个值,则将控制转移到目标指令(短格式)。 |
Bgt | 若是第一个值大于第二个值,则将控制转移到目标指令。 |
Bgt.S | 若是第一个值大于第二个值,则将控制转移到目标指令(短格式)。 |
Bgt.Un | 当比较无符号整数值或不可排序的浮点型值时,若是第一个值大于第二个值,则将控制转移到目标指令。 |
Bgt.Un.S | 当比较无符号整数值或不可排序的浮点型值时,若是第一个值大于第二个值,则将控制转移到目标指令(短格式)。 |
Ble | 若是第一个值小于或等于第二个值,则将控制转移到目标指令。 |
Ble.S | 若是第一个值小于或等于第二个值,则将控制转移到目标指令(短格式)。 |
Ble.Un | 当比较无符号整数值或不可排序的浮点型值时,若是第一个值小于或等于第二个值,则将控制转移到目标指令。 |
Ble.Un.S | 当比较无符号整数值或不可排序的浮点值时,若是第一个值小于或等于第二个值,则将控制权转移到目标指令(短格式)。 |
Blt | 若是第一个值小于第二个值,则将控制转移到目标指令。 |
Blt.S | 若是第一个值小于第二个值,则将控制转移到目标指令(短格式)。 |
Blt.Un | 当比较无符号整数值或不可排序的浮点型值时,若是第一个值小于第二个值,则将控制转移到目标指令。 |
Blt.Un.S | 当比较无符号整数值或不可排序的浮点型值时,若是第一个值小于第二个值,则将控制转移到目标指令(短格式)。 |
Bne.Un | 当两个无符号整数值或不可排序的浮点型值不相等时,将控制转移到目标指令。 |
Bne.Un.S | 当两个无符号整数值或不可排序的浮点型值不相等时,将控制转移到目标指令(短格式)。 |
Box | 将值类转换为对象引用(O 类型)。 |
Br | 无条件地将控制转移到目标指令。 |
Br.S | 无条件地将控制转移到目标指令(短格式)。 |
Break | 向公共语言结构 (CLI) 发出信号以通知调试器已撞上了一个断点。 |
Brfalse | 若是 value 为 false、空引用(Visual Basic 中的 Nothing)或零,则将控制转移到目标指令。 |
Brfalse.S | 若是 value 为 false、空引用或零,则将控制转移到目标指令。 |
Brtrue | 若是 value 为 true、非空或非零,则将控制转移到目标指令。 |
Brtrue.S | 若是 value 为 true、非空或非零,则将控制转移到目标指令(短格式)。 |
Call | 调用由传递的方法说明符指示的方法。 |
Calli | 经过调用约定描述的参数调用在计算堆栈上指示的方法(做为指向入口点的指针)。 |
Callvirt | 对对象调用后期绑定方法,而且将返回值推送到计算堆栈上。 |
Castclass | 尝试将引用传递的对象转换为指定的类。 |
Ceq | 比较两个值。若是这两个值相等,则将整数值 1 (int32) 推送到计算堆栈上;不然,将 0 (int32) 推送到计算堆栈上。 |
Cgt | 比较两个值。若是第一个值大于第二个值,则将整数值 1 (int32) 推送到计算堆栈上;反之,将 0 (int32) 推送到计算堆栈上。 |
Cgt.Un | 比较两个无符号的或不可排序的值。若是第一个值大于第二个值,则将整数值 1 (int32) 推送到计算堆栈上;反之,将 0 (int32) 推送到计算堆栈上。 |
Ckfinite | 若是值不是有限数,则引起 ArithmeticException。 |
Clt | 比较两个值。若是第一个值小于第二个值,则将整数值 1 (int32) 推送到计算堆栈上;反之,将 0 (int32) 推送到计算堆栈上。 |
Clt.Un | 比较无符号的或不可排序的值 value1 和 value2。若是 value1 小于 value2,则将整数值 1 (int32 ) 推送到计算堆栈上;反之,将 0 ( int32 ) 推送到计算堆栈上。 |
Constrained | 约束要对其进行虚方法调用的类型。 |
Conv.I | 将位于计算堆栈顶部的值转换为 native int。 |
Conv.I1 | 将位于计算堆栈顶部的值转换为 int8,而后将其扩展(填充)为 int32。 |
Conv.I2 | 将位于计算堆栈顶部的值转换为 int16,而后将其扩展(填充)为 int32。 |
Conv.I4 | 将位于计算堆栈顶部的值转换为 int32。 |
Conv.I8 | 将位于计算堆栈顶部的值转换为 int64。 |
Conv.Ovf.I | 将位于计算堆栈顶部的有符号值转换为有符号 native int,并在溢出时引起 OverflowException。 |
Conv.Ovf.I.Un | 将位于计算堆栈顶部的无符号值转换为有符号 native int,并在溢出时引起 OverflowException。 |
Conv.Ovf.I1 | 将位于计算堆栈顶部的有符号值转换为有符号 int8 并将其扩展为 int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.I1.Un | 将位于计算堆栈顶部的无符号值转换为有符号 int8 并将其扩展为 int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.I2 | 将位于计算堆栈顶部的有符号值转换为有符号 int16 并将其扩展为 int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.I2.Un | 将位于计算堆栈顶部的无符号值转换为有符号 int16 并将其扩展为 int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.I4 | 将位于计算堆栈顶部的有符号值转换为有符号 int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.I4.Un | 将位于计算堆栈顶部的无符号值转换为有符号 int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.I8 | 将位于计算堆栈顶部的有符号值转换为有符号 int64,并在溢出时引起 OverflowException。 |
Conv.Ovf.I8.Un | 将位于计算堆栈顶部的无符号值转换为有符号 int64,并在溢出时引起 OverflowException。 |
Conv.Ovf.U | 将位于计算堆栈顶部的有符号值转换为 unsigned native int,并在溢出时引起 OverflowException。 |
Conv.Ovf.U.Un | 将位于计算堆栈顶部的无符号值转换为 unsigned native int,并在溢出时引起 OverflowException。 |
Conv.Ovf.U1 | 将位于计算堆栈顶部的有符号值转换为 unsigned int8 并将其扩展为 int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.U1.Un | 将位于计算堆栈顶部的无符号值转换为 unsigned int8 并将其扩展为 int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.U2 | 将位于计算堆栈顶部的有符号值转换为 unsigned int16 并将其扩展为 int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.U2.Un | 将位于计算堆栈顶部的无符号值转换为 unsigned int16 并将其扩展为 int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.U4 | 将位于计算堆栈顶部的有符号值转换为 unsigned int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.U4.Un | 将位于计算堆栈顶部的无符号值转换为 unsigned int32,并在溢出时引起 OverflowException。 |
Conv.Ovf.U8 | 将位于计算堆栈顶部的有符号值转换为 unsigned int64,并在溢出时引起 OverflowException。 |
Conv.Ovf.U8.Un | 将位于计算堆栈顶部的无符号值转换为 unsigned int64,并在溢出时引起 OverflowException。 |
Conv.R.Un | 将位于计算堆栈顶部的无符号整数值转换为 float32。 |
Conv.R4 | 将位于计算堆栈顶部的值转换为 float32。 |
Conv.R8 | 将位于计算堆栈顶部的值转换为 float64。 |
Conv.U | 将位于计算堆栈顶部的值转换为 unsigned native int,而后将其扩展为 native int。 |
Conv.U1 | 将位于计算堆栈顶部的值转换为 unsigned int8,而后将其扩展为 int32。 |
Conv.U2 | 将位于计算堆栈顶部的值转换为 unsigned int16,而后将其扩展为 int32。 |
Conv.U4 | 将位于计算堆栈顶部的值转换为 unsigned int32,而后将其扩展为 int32。 |
Conv.U8 | 将位于计算堆栈顶部的值转换为 unsigned int64,而后将其扩展为 int64。 |
Cpblk | 将指定数目的字节从源地址复制到目标地址。 |
Cpobj | 将位于对象(&、* 或 native int 类型)地址的值类型复制到目标对象(&、* 或 native int 类型)的地址。 |
Div | 将两个值相除并将结果做为浮点(F 类型)或商(int32 类型)推送到计算堆栈上。 |
Div.Un | 两个无符号整数值相除并将结果 ( int32 ) 推送到计算堆栈上。 |
Dup | 复制计算堆栈上当前最顶端的值,而后将副本推送到计算堆栈上。 |
Endfilter | 将控制从异常的 filter 子句转移回公共语言结构 (CLI) 异常处理程序。 |
Endfinally | 将控制从异常块的 fault 或 finally 子句转移回公共语言结构 (CLI) 异常处理程序。 |
Initblk | 将位于特定地址的内存的指定块初始化为给定大小和初始值。 |
Initobj | 将位于指定地址的值类型的每一个字段初始化为空引用或适当的基元类型的 0。 |
Isinst | 测试对象引用(O 类型)是否为特定类的实例。 |
Jmp | 退出当前方法并跳至指定方法。 |
Ldarg | 将参数(由指定索引值引用)加载到堆栈上。 |
Ldarg.0 | 将索引为 0 的参数加载到计算堆栈上。 |
Ldarg.1 | 将索引为 1 的参数加载到计算堆栈上。 |
Ldarg.2 | 将索引为 2 的参数加载到计算堆栈上。 |
Ldarg.3 | 将索引为 3 的参数加载到计算堆栈上。 |
Ldarg.S | 将参数(由指定的短格式索引引用)加载到计算堆栈上。 |
Ldarga | 将参数地址加载到计算堆栈上。 |
Ldarga.S | 以短格式将参数地址加载到计算堆栈上。 |
Ldc.I4 | 将所提供的 int32 类型的值做为 int32 推送到计算堆栈上。 |
Ldc.I4.0 | 将整数值 0 做为 int32 推送到计算堆栈上。 |
Ldc.I4.1 | 将整数值 1 做为 int32 推送到计算堆栈上。 |
Ldc.I4.2 | 将整数值 2 做为 int32 推送到计算堆栈上。 |
Ldc.I4.3 | 将整数值 3 做为 int32 推送到计算堆栈上。 |
Ldc.I4.4 | 将整数值 4 做为 int32 推送到计算堆栈上。 |
Ldc.I4.5 | 将整数值 5 做为 int32 推送到计算堆栈上。 |
Ldc.I4.6 | 将整数值 6 做为 int32 推送到计算堆栈上。 |
Ldc.I4.7 | 将整数值 7 做为 int32 推送到计算堆栈上。 |
Ldc.I4.8 | 将整数值 8 做为 int32 推送到计算堆栈上。 |
Ldc.I4.M1 | 将整数值 -1 做为 int32 推送到计算堆栈上。 |
Ldc.I4.S | 将提供的 int8 值做为 int32 推送到计算堆栈上(短格式)。 |
Ldc.I8 | 将所提供的 int64 类型的值做为 int64 推送到计算堆栈上。 |
Ldc.R4 | 将所提供的 float32 类型的值做为 F (float) 类型推送到计算堆栈上。 |
Ldc.R8 | 将所提供的 float64 类型的值做为 F (float) 类型推送到计算堆栈上。 |
Ldelem | 按照指令中指定的类型,将指定数组索引中的元素加载到计算堆栈的顶部。 |
Ldelem.I | 将位于指定数组索引处的 native int 类型的元素做为 native int 加载到计算堆栈的顶部。 |
Ldelem.I1 | 将位于指定数组索引处的 int8 类型的元素做为 int32 加载到计算堆栈的顶部。 |
Ldelem.I2 | 将位于指定数组索引处的 int16 类型的元素做为 int32 加载到计算堆栈的顶部。 |
Ldelem.I4 | 将位于指定数组索引处的 int32 类型的元素做为 int32 加载到计算堆栈的顶部。 |
Ldelem.I8 | 将位于指定数组索引处的 int64 类型的元素做为 int64 加载到计算堆栈的顶部。 |
Ldelem.R4 | 将位于指定数组索引处的 float32 类型的元素做为 F 类型(浮点型)加载到计算堆栈的顶部。 |
Ldelem.R8 | 将位于指定数组索引处的 float64 类型的元素做为 F 类型(浮点型)加载到计算堆栈的顶部。 |
Ldelem.Ref | 将位于指定数组索引处的包含对象引用的元素做为 O 类型(对象引用)加载到计算堆栈的顶部。 |
Ldelem.U1 | 将位于指定数组索引处的 unsigned int8 类型的元素做为 int32 加载到计算堆栈的顶部。 |
Ldelem.U2 | 将位于指定数组索引处的 unsigned int16 类型的元素做为 int32 加载到计算堆栈的顶部。 |
Ldelem.U4 | 将位于指定数组索引处的 unsigned int32 类型的元素做为 int32 加载到计算堆栈的顶部。 |
Ldelema | 将位于指定数组索引的数组元素的地址做为 & 类型(托管指针)加载到计算堆栈的顶部。 |
Ldfld | 查找对象中其引用当前位于计算堆栈的字段的值。 |
Ldflda | 查找对象中其引用当前位于计算堆栈的字段的地址。 |
Ldftn | 将指向实现特定方法的本机代码的非托管指针(native int 类型)推送到计算堆栈上。 |
Ldind.I | 将 native int 类型的值做为 native int 间接加载到计算堆栈上。 |
Ldind.I1 | 将 int8 类型的值做为 int32 间接加载到计算堆栈上。 |
Ldind.I2 | 将 int16 类型的值做为 int32 间接加载到计算堆栈上。 |
Ldind.I4 | 将 int32 类型的值做为 int32 间接加载到计算堆栈上。 |
Ldind.I8 | 将 int64 类型的值做为 int64 间接加载到计算堆栈上。 |
Ldind.R4 | 将 float32 类型的值做为 F (float) 类型间接加载到计算堆栈上。 |
Ldind.R8 | 将 float64 类型的值做为 F (float) 类型间接加载到计算堆栈上。 |
Ldind.Ref | 将对象引用做为 O(对象引用)类型间接加载到计算堆栈上。 |
Ldind.U1 | 将 unsigned int8 类型的值做为 int32 间接加载到计算堆栈上。 |
Ldind.U2 | 将 unsigned int16 类型的值做为 int32 间接加载到计算堆栈上。 |
Ldind.U4 | 将 unsigned int32 类型的值做为 int32 间接加载到计算堆栈上。 |
Ldlen | 将从零开始的、一维数组的元素的数目推送到计算堆栈上。 |
Ldloc | 将指定索引处的局部变量加载到计算堆栈上。 |
Ldloc.0 | 将索引 0 处的局部变量加载到计算堆栈上。 |
Ldloc.1 | 将索引 1 处的局部变量加载到计算堆栈上。 |
Ldloc.2 | 将索引 2 处的局部变量加载到计算堆栈上。 |
Ldloc.3 | 将索引 3 处的局部变量加载到计算堆栈上。 |
Ldloc.S | 将特定索引处的局部变量加载到计算堆栈上(短格式)。 |
Ldloca | 将位于特定索引处的局部变量的地址加载到计算堆栈上。 |
Ldloca.S | 将位于特定索引处的局部变量的地址加载到计算堆栈上(短格式)。 |
Ldnull | 将空引用(O 类型)推送到计算堆栈上。 |
Ldobj | 将地址指向的值类型对象复制到计算堆栈的顶部。 |
Ldsfld | 将静态字段的值推送到计算堆栈上。 |
Ldsflda | 将静态字段的地址推送到计算堆栈上。 |
Ldstr | 推送对元数据中存储的字符串的新对象引用。 |
Ldtoken | 将元数据标记转换为其运行时表示形式,并将其推送到计算堆栈上。 |
Ldvirtftn | 将指向实现与指定对象关联的特定虚方法的本机代码的非托管指针(native int 类型)推送到计算堆栈上。 |
Leave | 退出受保护的代码区域,无条件将控制转移到特定目标指令。 |
Leave.S | 退出受保护的代码区域,无条件将控制转移到目标指令(缩写形式)。 |
Localloc | 从本地动态内存池分配特定数目的字节并将第一个分配的字节的地址(瞬态指针,* 类型)推送到计算堆栈上。 |
Mkrefany | 将对特定类型实例的类型化引用推送到计算堆栈上。 |
Mul | 将两个值相乘并将结果推送到计算堆栈上。 |
Mul.Ovf | 将两个整数值相乘,执行溢出检查,并将结果推送到计算堆栈上。 |
Mul.Ovf.Un | 将两个无符号整数值相乘,执行溢出检查,并将结果推送到计算堆栈上。 |
Neg | 对一个值执行求反并将结果推送到计算堆栈上。 |
Newarr | 将对新的从零开始的一维数组(其元素属于特定类型)的对象引用推送到计算堆栈上。 |
Newobj | 建立一个值类型的新对象或新实例,并将对象引用(O 类型)推送到计算堆栈上。 |
Nop | 若是修补操做码,则填充空间。尽管可能消耗处理周期,但未执行任何有意义的操做。 |
Not | 计算堆栈顶部整数值的按位求补并将结果做为相同的类型推送到计算堆栈上。 |
Or | 计算位于堆栈顶部的两个整数值的按位求补并将结果推送到计算堆栈上。 |
Pop | 移除当前位于计算堆栈顶部的值。 |
Prefix1 | 基础结构。此指令为保留指令。 |
Prefix2 | 基础结构。此指令为保留指令。 |
Prefix3 | 基础结构。此指令为保留指令。 |
Prefix4 | 基础结构。此指令为保留指令。 |
Prefix5 | 基础结构。此指令为保留指令。 |
Prefix6 | 基础结构。此指令为保留指令。 |
Prefix7 | 基础结构。此指令为保留指令。 |
Prefixref | 基础结构。此指令为保留指令。 |
Readonly | 指定后面的数组地址操做在运行时不执行类型检查,而且返回可变性受限的托管指针。 |
Refanytype | 检索嵌入在类型化引用内的类型标记。 |
Refanyval | 检索嵌入在类型化引用内的地址(& 类型)。 |
Rem | 将两个值相除并将余数推送到计算堆栈上。 |
Rem.Un | 将两个无符号值相除并将余数推送到计算堆栈上。 |
Ret | 从当前方法返回,并将返回值(若是存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。 |
Rethrow | 再次引起当前异常。 |
Shl | 将整数值左移(用零填充)指定的位数,并将结果推送到计算堆栈上。 |
Shr | 将整数值右移(保留符号)指定的位数,并将结果推送到计算堆栈上。 |
Shr.Un | 将无符号整数值右移(用零填充)指定的位数,并将结果推送到计算堆栈上。 |
Sizeof | 将提供的值类型的大小(以字节为单位)推送到计算堆栈上。 |
Starg | 将位于计算堆栈顶部的值存储到位于指定索引的参数槽中。 |
Starg.S | 将位于计算堆栈顶部的值存储在参数槽中的指定索引处(短格式)。 |
Stelem | 用计算堆栈中的值替换给定索引处的数组元素,其类型在指令中指定。 |
Stelem.I | 用计算堆栈上的 native int 值替换给定索引处的数组元素。 |
Stelem.I1 | 用计算堆栈上的 int8 值替换给定索引处的数组元素。 |
Stelem.I2 | 用计算堆栈上的 int16 值替换给定索引处的数组元素。 |
Stelem.I4 | 用计算堆栈上的 int32 值替换给定索引处的数组元素。 |
Stelem.I8 | 用计算堆栈上的 int64 值替换给定索引处的数组元素。 |
Stelem.R4 | 用计算堆栈上的 float32 值替换给定索引处的数组元素。 |
Stelem.R8 | 用计算堆栈上的 float64 值替换给定索引处的数组元素。 |
Stelem.Ref | 用计算堆栈上的对象 ref 值(O 类型)替换给定索引处的数组元素。 |
Stfld | 用新值替换在对象引用或指针的字段中存储的值。 |
Stind.I | 在所提供的地址存储 native int 类型的值。 |
Stind.I1 | 在所提供的地址存储 int8 类型的值。 |
Stind.I2 | 在所提供的地址存储 int16 类型的值。 |
Stind.I4 | 在所提供的地址存储 int32 类型的值。 |
Stind.I8 | 在所提供的地址存储 int64 类型的值。 |
Stind.R4 | 在所提供的地址存储 float32 类型的值。 |
Stind.R8 | 在所提供的地址存储 float64 类型的值。 |
Stind.Ref | 存储所提供地址处的对象引用值。 |
Stloc | 从计算堆栈的顶部弹出当前值并将其存储到指定索引处的局部变量列表中。 |
Stloc.0 | 从计算堆栈的顶部弹出当前值并将其存储到索引 0 处的局部变量列表中。 |
Stloc.1 | 从计算堆栈的顶部弹出当前值并将其存储到索引 1 处的局部变量列表中。 |
Stloc.2 | 从计算堆栈的顶部弹出当前值并将其存储到索引 2 处的局部变量列表中。 |
Stloc.3 | 从计算堆栈的顶部弹出当前值并将其存储到索引 3 处的局部变量列表中。 |
Stloc.S | 从计算堆栈的顶部弹出当前值并将其存储在局部变量列表中的 index 处(短格式)。 |
Stobj | 将指定类型的值从计算堆栈复制到所提供的内存地址中。 |
Stsfld | 用来自计算堆栈的值替换静态字段的值。 |
Sub | 从其余值中减去一个值并将结果推送到计算堆栈上。 |
Sub.Ovf | 从另外一值中减去一个整数值,执行溢出检查,而且将结果推送到计算堆栈上。 |
Sub.Ovf.Un | 从另外一值中减去一个无符号整数值,执行溢出检查,而且将结果推送到计算堆栈上。 |
Switch | 实现跳转表。 |
Tailcall | 执行后缀的方法调用指令,以便在执行实际调用指令前移除当前方法的堆栈帧。 |
Throw | 引起当前位于计算堆栈上的异常对象。 |
Unaligned | 指示当前位于计算堆栈上的地址可能没有与紧接的 ldind、stind、ldfld、stfld、ldobj、stobj、initblk 或 cpblk 指令的天然大小对齐。 |
Unbox | 将值类型的已装箱的表示形式转换为其未装箱的形式。 |
Unbox.Any | 将指令中指定类型的已装箱的表示形式转换成未装箱形式。 |
Volatile | 指定当前位于计算堆栈顶部的地址能够是易失的,而且读取该位置的结果不能被缓存,或者对该地址的多个存储区不能被取消。 |
Xor | 计算位于计算堆栈顶部的两个值的按位异或,而且将结果推送到计算堆栈上。 |
引用资料:
http://www.cnblogs.com/zery/p/3368460.html
https://msdn.microsoft.com/en-us/library/812xyxy2
做者:马三小伙儿
出处:http://www.cnblogs.com/msxh/p/7819645.html请尊重别人的劳动成果,让分享成为一种美德,欢迎转载。另外,文章在表述和代码方面若有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!