▶1.程序的解释执行方式

程序语言强写的计策机指令序列称为“源程序”,计算机并不能直接执行用高级语言编写的源程序,源程序必须通过“翻译程序”翻译成机器指令的形式,计算机才能项别和执行。源程序的翻译有两种方式:解释执行和编译执行。不同的程序语言,有不同的翻译程序,这些翻译程序称为程序解释器(也称为虚拟机)或程序编译器(简称为编译器)。

1)程序的解释执行过程

解释程序的工作过程如下:首先,由语言解释器(如Python)进行初始化准备工作。然后语言解释器从源程序中读取一个语句(指令),并对指令进行语法检查,如果程序语法有错,则输出错误信息;否则,将源程序语句翻译成机器执行指令,并执行相应的机器操作。返回后检查解释工作是否完成,如果未完成,语言解释器继续解释下一语句,直至整个程序执行完成。否则,进行必要的善后处理工作。

语言解释器一般包含在开发软件或操作系统内,如IE浏览器带有.Net脚本语言解释功能;也有些语言解释器是独立的,如Python解释器就包含在Python软件包中。

2)解释程序的特点

解释程序的优点是实现简单,交互性较好。动态程序语言(如Python、PHP、JavaScript、R、MATLIB等)一般采用解释执行方式。
解释程序有以下缺点:一是程序运行效率低,如源程序中出现循环语句时,解释程序也要重复地解释并执行这一组语句;二是程序的独立性不强,不能在操作系统下直接运行,因为操作系统不一定提供这个语言的解释器;三是程序代码保密性不强,例如,要发布Python开发项目,实际上就是发布Python源代码。

▶2.程序的编译执行方式

程序员编写好源程序后,由编译器将源程序翻译成计算机可执行的机器代码。程序编译完成后就不再需要再次编译了,生成的机器代码可以反复执行。

源程序编译是一个复杂的过程,这一过程分为以下步骤:源程序→预处理→词法分析→语法分析→语义分析→生成中间代码→代码优化→生成目标程序→程序连接→生成可执行程序。事实上,某些步骤可能组合在一起进行。

在编译过程中,源程序的各种信息被保存在不同表格里,编译工作的各个阶段都涉及构造、查找或更新有关表格。如果编译过程中发现源程序有错误,编译器会报告错误的性质和发生错误的代码行,这些工作称为出错处理。

1)预处理

一个源程序有时可能分成几个模块存放在不同的文件里,预处理的工作之一是将这些源程序汇集到一起;其次,为了加快编译速度,编译器往往需要提前对一些头文件及程序代码进行预处理,以便在源程序正式编译时节省系统资源开销。例如,C语言的预处理包括文件合并、宏定义展开、文件包含、条件编译等内容。

2)词法分析

编译器的功能是解释程序文本的语义,不幸的是计算机很难理解文本,文本文件对计算机来说就是字节序列,为了理解文本的含义,就需要借助词法分析程序。词法分析是将源程序的字符序列转换为标记(Token)序列的过程。词法分析的过程是编译器一个字符一个字符地读取源程序,然后对源程序字符流进行扫描和分解,从而识别出一个个独立的单词或符号(分词)。在词法分析过程中,编译器还会对标记进行分类。
单词是程序语言的基本语法单位,一般有四类单词:一是语言定义的关键字或保留字(如if、for等);二是标识符(如x、i、list等);三是常量(如0、3.14159等);四是运算符和分界符(如十、一、*、/、=、;等)。如何进行“分词”是词法分析的重要工作。

3)语法分析

语法分析过程是把词法分析产生的单词,根据程序语言的语法规则,生成抽象语法树(AST),语法树是程序语句的树形结构表示,编译器将利用语法树进行语法规则分析。语法树的每一个节点都代表着程序代码中的一个语法结构,例如包、类型、标识符、表达式、运算符、返回值等。后续的工作是对抽象语法树进行分析。

符号表是由一组符号地址和符号信息构成的表格。符号表中登记的信息在编译的不同阶段都要用到。在语法分析中,符号表登记的内容将用于语法分析检查;在语义分析中,符号表所登记的内容将用于语义检查和产生中间代码;在目标代码生成阶段,当对符号名进行地址分配时,符号表是地址分配的依据。

4)语义分析

语义分析是对源程序的上下文进行检查,审查有无语义错误。语义分析主要任务有静态语义审查、上下文相关性审查、类型匹配审查、数据类型转换、表达式常量折叠等。
源程序中有些语句按照语法规则判断是正确的,但是它不符合语义规则。例如,使用了没有声明的变量;或者对一个过程名赋值;或者调用函数时参数类型不合适;或者参加运算的两个变量类型不匹配等。当源程序不符合语言规范时,编译器会报告出错信息。
表达式常量折叠就是对常量表达式计算求值,并用求得的值来替换表达式,放入常量表。例如,s=1+2折叠之后为常量3,这也是一种编译优化。

5)生成中间代码

语义分析正确后,编译器会生成相应的中间代码。中间代码是一种介于源程序和目标代码之间的中间语言形式,它的目的是:便于后面做优化处理,便于程序的移植。中间代码常见形式有四元式、三元式、逆波兰表达式等。由中间代码很容易生成目标代码。

6)代码优化

代码优化的目的是为了得到高质量的目标程序。

7)生成目标程序

生成目标程序不仅与编译技术有关,而且与机器硬件结构关系密切。例如,充分利用机器的硬件资源,减少对内存的访问次数;根据机器硬件特点(如多核CPU)调整目标代码,提高执行效率。生成目标程序的过程实际上是把中间代码翻译成汇编指令的过程。

8)链接程序

目标程序还不能直接执行,因为程序中可能还有许多没有解决的问题。例如,源程序可能调用了某个库函数等。链接程序的主要工作就是将目标文件和函数库彼此连接,生成一个能够让操作系统执行的机器代码文件(软件)。

9)生成可执行程序(机器代码)

机器代码生成是编译过程的最后阶段。机器代码生成不仅仅需要将前面各个步骤所生成的信息(语法树、符号表、目标程序等)转化成机器代码写入到磁盘中,编译器还会进行少量的代码添加和转换工作。经过上述过程后,源程序最终转换成可执行文件了。

▶3.程序编译失败的主要原因

完美的程序不会一次就写成功,都需要经过反复修改、调试和编译。Google和香港科技大学的研究人员分析了Google工程师的2600万次编译,分析了编译失败的常见原因:一是编译失败率与编译次数、开发者经验无关;二是大约65%的Java编译错误与依赖有关,如编译器无法找到一个符号(占编译错误的43%),或者是包文件不存在;在C++编译中,53%的编译错误是使用了未声明的标识符和不存在的类变量。

12-09 01:08