您的位置:首页 > 网页编程 > linux >
编译器的内部工作原理
时间:2013-11-18 23:53来源:未知 作者:66php 点击:
/**谷歌广告**/

    编译器的内部工作原理,有了语言的参考手册,下面让我们来研究一下根据手册如何理解程序的过程。首先让我们看一看程序员在阅读和理解程序时都完成了哪些工作。
    (1)人工翻译的过程。当程序员在阅读这段代码时,他们能够得到什么信息呢?又是怎样得到这些信息的呢?熟练的程序员通常会首先浏览整个程序的大致结构,发现整个程序包含3个相对独立的定义——即包含了一个全局变量定义和两个函数定义。然后,他会查看变量定义和函数定义的具体内容,获得以下的信息。
①该程序包含一个全局变量定义a,该变量是整型的。
②包含两个函数定义。函数add的返回值是整型,且接收两个整型参数,并将两个输入参数的和作为返回值传递给调用方。
③main是主函数,没有参数且返回值是整型。main函数中将变量a赋值为5,并以a和整型常量6为参数调用add函数,结果赋值给变量a。main函数最后返回0
    上面的这些信息已经足够我们理解该程序的含义。熟悉C语言和汇编语言的读者也许已经可以很容易地将该C语言程序翻译为具有相同功能的汇编程序。图中所示的模型表明,程序员在进行翻译时完成了两个任务,即程序的分析(从源程序中提取必要的信息)和程序的转换(根据搜集的程序信息和对汇编语言或硬件指令集的理解,编写等价的结果程序)。这两个阶段通常又称为程序的分析和程序的综合。而从源程序中提取的相关信息是使这两个任务有机结合为一个整体的基础。程序员会采用多种灵活的方式来记录源程序中的信息,如可以列出关键函数表、全局变量表等。此外,一些细节信息则可以通过对源程序的搜索来回顾。例如,看见变量a之后如果想不起来其具体的定义形式,则可以回头去看a被定义的地方。  
    (2)程序的自动翻译过程。编译器在执行编译任务时,要完成的工作和人工翻译时非常类似。因此,编译器也需要通过对源程序的分析来搜集程序的结构与细节信息,然后根据这些信息和对目标语言的理解产生编译结果。然而,要让编译器从程序源代码中自动提取出这些程序含义的信息却并不是一件容易的事情。实际上,如果将程序换一个写法,即使程序员来阅读这段程序也会感到非常困难。
    int a;int add(int d1,int d2){return d1+d2;}int main(void){a=5;a=add(a, 6);return 0;}  
    这段程序和上面的程序在内容上完全一样,只是缩进、分行排版格式等被完全删除了。而这正是编译器所看到的程序的样子—纯粹的符号序列,没有可以提供直观视觉帮助的排版格式(前面的程序里可以一眼看出int main(void)是函数的定义,但这里则不得不一个字一个字地去分析才能知道)。另一方面,一些对于程序员来说非常自然的任务如果让编译器来做,也会更为复杂。像记录函数定义这样的事情,程序员只需拿起纸和笔就可以了,而编译器必须为记录这些信息提供相应的数据结构。由于这些原因,编译器在分析和翻译时,通常是按与下面过程类似的方式工作的。  
① 源程序的分析过程:首先,编译器读进一个单词int3。根据对C语言的理解,该单词或者是一个全局变量定义的开头,或者是某个函数定义的开头。然后,编译器读进名字a和分号,并由此判断该语句是一个变量定义语句,定义了一个整型变量a。为了保障全局变量a没有重复定义,编译器应记录所有变量定义信息以供检查。  编译器读进下一个int、add,并遇到左括号“(”。此时,C语言编译器可以确定遇到的是一个函数定义,函数名为add,返回值是整型。而后,编译器识别出add的参数个数、每个参数的类型及名称等信息。当遇到“{”时,继续识别add函数内的各条语句。  识别完add函数后,即可继续识别后面的变量定义和函数定义等代码。
 ② 逐词分析和完整分析:这里需要特别指出一点,无论是程序员还是编译器,当看到“int add(”时由于还没有看到后面的内容,因此无法确定一定遇到的是完整、正确的函数定义(如程序员不小心写成“int add(int d1; int d2)”)。这样,只有当看到了完整的“int add(int d1, int d2)”时才能得出准确的结论。由此可见,编译器在对程序进行分析时不能看了后面忘记前面,即编译器不仅要能够知道正在看到的单词的含义,更要能够将后面遇到的内容和之前已经识别的内容合起来作为一个整体检查,才可以得到正确的结论。  在这种不断检查和匹配的过程中,编译器就可以知道程序、定义、语句等的结构是否完整,从而判断程序在格式上的合法性。  
③ 信息搜集过程:另一方面,为了判断像变量重复定义、变量赋值类型不匹配等不属于格式错误的问题,编译器还需要在分析的过程中搜集有关变量、函数定义形式、函数参数个数及其类型等方面的信息,并以某种形式将这些信息保存下来。和上面的情况类似,由于编译器在识别程序时是一个符号一个符号进行分析的,因此要确定函数add定义的所有信息,就必须将“int add(int d1, int d2)”全部识别完后才能进行记录。而关于函数名“add”、函数返回值“int”、函数的两个参数“int d1”、“int d2”的识别都是分别完成的。因此,编译器设计者需要提供一种机制,将这些在不同步骤中识别出来的零散信息整合到一起,供后续识别和翻译使用。
④ 翻译过程:由于高级语言程序中的很多语言结构不能直接映射到CPU指令集合中,因此编译器必须知道每种语言成分应当如何用所需CPU的指令来实现。例如,C语言的if-then-else语句可以用一系列的简单运算指令和几个条件转移指令共同实现。由此可见,翻译过程通常不是直接进行的。编译器通常不能仅仅依靠当前所看到的程序片段产生所需的翻译结果,而是要等到所需信息都知道后才能进行翻译(注意,编译器扫描程序是按顺序进行的,因此必须将前面扫描中识别的信息记到内部数据结构中,才能在后面的分析和综合过程中获得足够的信息)。这与信息搜集时的情况非常类似。  

    100%
    (2)
    0%
    (0)
    最新评论
    选择评论类型:
    验证码:点击我更换图片
    本站推荐
    /**谷歌广告**/
    关于我们 | 网站地图 | rss地图 | 广告服务 | vip源码 | 联系我们
    Powered by 66php Copyright 2011-2013
    苏ICP备11045037号