C#基本知识的一点感悟(2)——从源代码到中间语言

        在对于C#基本知识的一点感悟(1)——关于C#语法的抽象中,我主要对C#的基本语法进行了抽象,重点介绍了MS构造整个基本的C#语法组织的思路(个人的猜测)。主要是在如何去组织源代码这一层去理解的C#。本篇接下来主要介绍从我们编辑好的源代码,到被C#编译器编译成.net组织代码的最基本单位----程序集之间发生的一些处理

        .net对源代码的处理模式 

        首先来看看对于一个高级语言编写的程序最终被cpu执行的一些过程吧。对于非托管的语言,比如C,首先是我们用C语言编写好自己的代码,当用编译器编译时,编译器先通过预处理,把我们代码里的头文件和源代码整合,得到完整的c文件;然后对这个完整的C文件进行编译,得到汇编代码。到汇编代码这一步,已经能够和cpu的大多数指令相对应了,比如对把函数里的变量放在寄存器里,比如过程调用时,对于参数、堆栈指针的保存、cpu计数器的保存、以及cpu计数器的地址跳转到被调用的函数等等一系列操作(这已经和cpu表面的执行指令很接近了吧)。当然,对于由多个文件组成的程序,要分别对每一个文件编译得到对应的汇编代码,然后把这个每一个汇编代码在通过汇编编译器编译为机器代码,当然这些包含了代码和数据的机器码(可重定位目标文件)还不能直接执行,很明显我们需要把这好几个的机器码给结合起来。这就经过一个叫链接的步骤(对于链接,主要对程序全局变量的协调处理,以及对于每一个里可重定位目标文件里符号系统的解析,最终得到一个经过整合过的可执行文件,这个文件也就是我们编译C源代码得到的结果,这个可执行的文件可以直接被操作系统加载,然后不用再做处理就开始执行。

        不过,对于C#,或者是对于其他面向.net的托管语言,我们写好的C#源代码,

        首先是相应的编译器(对于C#就是C#编译器,对于VB,则是VB编译器)把源代码里编译成为托管模块(这只是编译过程中的中间产物),这个托管模块包含了

        1,一些和操作系统相关联的信息(pe,比如是面向32位系统还是64位系统)pe可理解为主要和系统关联起来的东西;

        2,包含了一些clr能够识别的信息(clr头),比如需要的clr版本,clr进入相关方法的信息,以及一些标记(clr通过这些标记来寻找或者执行一些操作,比如我们用C#里对某个类型引用某个和运行时相关的Attribute),clr头可以理解为托管模块和clr进行交互的的一些所需信息;

        3,还包含了一些元数据表,这个元数据表主要包含了托管模块(即我们编写的源代码里)定义的类型,类型的成员,以及模块所引用的类型和类型的成员。记录的方式,当然就是记录下每一个类型与该类型定义的部分的中间代码的关联,每一个类型的方法与该类型对应方法的定义的代码中间代码的关联,等等对C#各种语法的支持的记录。通过这个东西,clr就能通过一个类型名,去构造这个类型对象,如果要用这个类型名去实例化一个实例对象,也可以参照元数据里对该类型的相关成员的记录来构造该对象,同时元数据还能支持许多C#的高级特性,比如内存管理,垃圾回收等等。总之,元数据就是对源代码里类型、类型的成员(源代码里定义的,或者引用的)的类型,对应的IL代码的记录。

        4,还有中间代码IL。首先有一点要注意的,我们的源代码肯定不会每一句都被转换为IL,比如public class A{}。。它只是告诉了编译器我们定义了一个类,A及一些信息,编译器可通过元数据来记录下这个信息,但是,肯定不会为它生产IL,因为很明显public class A{}它不是一个可执行的语句。再比如,在A里定义字段:public int filed;它同样会被元数据记录,但不会产生IL,因为在运行时,这一句也不会执行。那那些源代码会被编译为IL呢?亦即方法里的代码,(方法的定义肯定也不能执行),只有方法里的代码,才是最终可以被cpu去执行的。有意思吧,这再次说明,面向对象只是我们组织代码的一种形式,而最终的执行,还是面向过程的。前面已经说过,这个元数据里同样有相关记录和IL关联起来,这样在调用某个方法时,clr才能找到这个方法对应的il所在的位置。但是,务必注意,这个IL也是面向对象的,比如A a=new A();这句话,它在clr里会有newobj这个相应的指令。所以,这个IL只是面向clr的,它只能被clr(一个com服务)来执行。至于clr的执行方式,呵呵,clr肯定要把对应的IL即时编译(JIT)为机器代码,最终由cpu来执行咯。

        说了这么多,只是涉及到各种语言被对应的语言编译器编译为托管模块,对于面向.net的不同语言,被编译成的托管模块的语法是一致的,也就是说,他们可以相互调用。但前面说了,这个托管模块只是中间产物,编译器会把这些托管模块,最终合并成一个程序集。也就是我们编译得到的最终产物。clr最终执行时是从程序集去开始的,所以程序集是我们编译好源代码后,得到的最小单位,也是进行版本控制,代码组织的最小单位。程序集分为可执行的比如一个.exe文件,和不可直接执行的比如.dll文件。注意,除了一个源文件不能被编译在多个程序集这个限制以外,程序集和源文件并没有必然的联系,一个项目可以被编译为不止一个程序集,同样,一个程序集可以引用另一个程序集里的类型、方法等等,实际就是一个程序集可以执行另一个程序集里的代码(如果后者里的方法对前者可见)。程序集到底包含哪些东西呢?刚说了,许多(也可以只有一个)托管模块,最终变成了一个程序集,而且这个变只是组织结构的一种整合,元数据啊,IL啊之类的东西都没有变化。同时,为了是自描述的,程序集必须要告诉clr它里面的信息,所以在那些托管模块之外,程序集额外有一个清单,清单用来描述程序集内部包含的组成部分以及对应的关联,以便于clr通过程序集定位到对应的元数据,甚至通过对应的元数据对应到相关的IL代码。

        OK,这个包含了许多IL,元数据、以及自描述的清单的东西,就是编译器把我们的源代码最终编译成的东西。它离最终的可执行的机器代码很远,但是,它确实是可以被clr来执行,被(clr)托管执行。

        这些处理貌似很复杂,是的,还好他们是编译器来完成的。但是,这些复杂的处理同样为.net去提供各种功能提供了支持,尤其是最终把我们的代码编程能被clr执行的样子,他们确保了clr对面向对象的支持,而不是如c那种只能单纯的方法调用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值