2.1.3 编译过程

为了用C/C++编程,应该了解编译过程的步骤和所需工具。某些语言(特别是C/C++)编译时,首先要对源代码执行预处理。预处理器(preprocessor)是一个简单的程序,它用程序员(利用预处理器指令)定义好的模式代替源代码中的模式。预处理器指令用来节省输入,增加代码的可读性。(C++程序设计并不鼓励多使用预处理指令,因为它可能会引起一些不易发现错误,这些将在本书的后面分析)。预处理过的代码通常存放在一个中间文件中。

编译一般分两遍进行。首先,对预处理过的代码进行语法分析。编译器把源代码分解成小的单元并把它们按树形结构组织起来。表达式“A+B”中的“A”、“+”和“B”就是语法分析树的叶子节点。

有时候会在编译的第一遍和第二遍之间使用全局优化器(global optimizer)来生成更短、更快的代码。

编译的第二遍,由代码生成器(code generator)遍历语法分析树,把树的每个节点转化成汇编语言或机器代码。如果代码生成器生成的是汇编语言,那么还必须用汇编器对其汇编。两种情况的最后结果都是生成目标模块(通常是,一个以.o或.obj为扩展名的文件)。有时也会在第二遍中使用窥孔优化器(peephole optimizer)从相邻一段代码中查找冗余汇编语句。

用“object”(目标)一词表示一段机器代码是一种不合适的选择,在面向对象程序设计之前这一名词就普遍使用了。在讨论编译时“object”与“goal”(目标)含义相同,而在面向对象程序设计中,它的意思是“一个有边界的事物”。

连接器(linker)把一组目标模块连接成为一个可执行程序,操作系统可以装载和运行它。当某个目标模块中的函数要引用另一目标模块中的函数或变量时,由连接器来处理这些引用;这就保证了所有需要的、在编译时存在的外部函数和变量仍然存在。连接器还要添加一个特殊的目标模块来完成程序启动任务。

连接器能搜索称为“库”的特殊文件来处理它的所有引用。库将一组目标模块包含在一个文件中。库由一个被称为库管理器(librarian)的程序来创建和维护。

2.1.3.1 静态类型检查

类型检查(type checking)是编译器在第一遍中完成的。类型检查是检查函数参数是否正确使用,以防止许多程序设计错误。由于类型检查是在编译阶段而不是程序运行阶段进行的,所以称之为静态类型检查(static type checking)。

某些面向对象的语言(如Java)也可在程序运行时作部分类型检查[动态类型检查(dynamic type checking)]。动态类型检查和静态类型检查结合使用,比仅仅使用静态类型检查更有效。但它也增加了程序执行的开销。

C++使用静态类型检查,因为C++语言不采用任何特殊的运行时支持来处理错误操作。静态类型检查在编译时就告知程序员类型被误用,从而加快了执行时的速度。通过对C++的学习,我们会看到C++语言的主要设计目标也是追求运行速度快,这与面向生产的编程语言C语言一样。

在C++里可以不使用静态类型检查。我们可以自己做动态类型检查—这只需要写一些代码。