文档介绍:第20章编译及预处理
,,使用的编译链接器是如何根据这些文件生成可执行文件的?编译链接的机理到底是什么,这是本章要学习的内容。对C语言来说,除了掌握必要的语法机制外,学好预处理命令也是写出高质量代码的前提。
编译流程
本书前面给出了很多示例代码,实际上,哪怕是像Hello,World这样简单的示例程序,都要经过编辑、预处理、编译、链接4个步骤,才能变成可执行程序,鼠标双击就弹出命令窗口,显示“Hello,World”。这也是一般C语言程序的编译流程,如所示。
编辑
编辑可能就是通常所说的“写代码”,用集成开发工具也好,用记事本也好,按C语言的语法规则组织一系列的源文件,主要有两种形式,,,也称头文件。
预处理
前面接触到的“#include”和“#define”都属于编译预处理,C语言允许在程序中用预处理指令写一些命令行。预处理器在编译器之前根据指令更改程序文本。编译器看到的是预处理器修改过的代码文本,C语言的编译预处理功能主要包括宏定义、文件包含和条件编译3种。预处理器对宏进行替换,并将所包含的头文件整体插入源文件中,为后面要进行的编译做好准备。
编译
编译器处理的对象是由单个c文件和其中递归包含的头文件组成的编译单元,一般来说,头文件是不直接参加编译的。编译器会将每个编译单元翻译成同名的二进制代码文件,在DOS和Windows环境下,,在Unix环境下,,此时,二进制代码文件是零散的,还不是可执行二进制文件。
错误检查大多是在编译阶段进行的,编译器主要进行语法分析,词法分析,产生目标代码并进行代码优化等处理。为全局变量和静态变量等分配内存,并检查函数是否已定义,如没有定义,是否有函数声明。函数声明通知编译器:该函数在本文件晚些时候定义,或者是在其他文件中定义。
链接
链接器将编译得到的零散的二进制代码文件组合成二进制可执行文件,主要完成下述两个工作,一是解析其他文件中函数引用或其他引用,二是解析库函数。
举例来说,某个程序由两个c文件组成,、,两个c文件和其中递归包含的头文件组成的两个编译单元,,,,,,,这个过程称为函数解析(resolve),由链接器完成。不仅仅是函数,变量(诸如有外部链接性的全局变量)也牵扯到解析的问题。,编译时不会产生错误,但链接时却会提示,有未解析的对象,据此可分析出问题出在编译阶段还是链接阶段。
程序错误
兴致勃勃地写完程序,编译链接,一大堆的错误提示,不要沮丧,再优秀的程序员也会犯错,有人说,程序编写的过程大部分的时间都是用在错误调试上。有时为了排除一个小问题,可能会几天几夜地跟踪代码,正因为如此,有人把问题找到并解决的刹那称为“痛苦的幸福”。
继续说明程序错误前,有个观点要说明:没有完美的程序,不存在没有缺陷的程序,如果一个程序运行很完美,那是因为它的缺陷到现在还没有被发现。同样,软件测试是为了发现程序中可能存在的问题,而不是证明程序没有错误。
错误分类
错误可分两大类,一是程序书写形式在某些方面不合C语言要求,称为语法错误,这种错误将会由编译器指明,是种比较容易修改的错误,二是程序书写本身没错,编译链接能够完成,但输出结果与预期不符,或着执行着便崩溃掉,称为逻辑错误。
细分下去,语法错误又可分为编译错误和链接错误,很明显,编译错误就是在程序编译阶段出的错误,而链接错误就是在程序链接阶段出的问题。
编译错误
如果文件中出现编译错误,编译器将给出错误信息,并指明错误所在的行,提示用户修改代码,编译错误主要有两类:
(1)语法问题,缺少符号,如缺分号,缺括号等,符号拼写不正确,一般来说,编译器都会指明错误所在行,但由于代码是彼此联系的,有时编译器给出的信息未必正确。一般来说,源程序中出错位置要么就是编译器提示位置,要么在提示位置之前,甚至是在前面很远的地方。另一个问题是有时一个实际错误会让编译器给出很多出错提示,所以,面对成百上千个错误提示时,不要害怕,没准修改一处代码,所有的问题都解决了。
(2)上下文关系有误,程序设计中有很多彼此关联的东西,比如变量要先创建再使用,有时编译器会发现某个变量尚未定义,便会提示出错。这种情况有时是因为变量名拼写有误,有时是因为确实忘了定义。
除了错误外,编译器