程序编译与预处理

      如果一个源程序由多个诸如A.c,B.h等的源文件组成,使用的编译链接器是如何根据这些文件生成可执行文件的?编译链接的机理到底是什么,这是本章要学习的内容。对C语言来说,除了掌握必要的语法机制外,学好预处理命令也是写出高质量代码的前提。
   
    很多示例代码,实际上,哪怕是像Hello,World这样简单的示例程序,都要经过编辑、预处理、编译、链接4个步骤,才能变成可执行程序,鼠标双击就弹出命令窗口,显示“Hello,World”。这也是一般C语言程序的编译流程,如所示。

     
     
      编辑可能就是通常所说的“写代码”,用集成开发工具也好,用记事本也好,按C语言的语法规则组织一系列的源文件,主要有两种形式,一种是.c文件,另一种是.h文件,也称头文件。
    
    “#include”和“#define”都属于编译预处理,C语言允许在程序中用预处理指令写一些命令行。预处理器在编译器之前根据指令更改程序文本。编译器看到的是预处理器修改过的代码文本,C语言的编译预处理功能主要包括宏定义、文件包含和条件编译3种。预处理器对宏进行替换,并将所包含的头文件整体插入源文件中,为后面要进行的编译做好准备。
    
      编译器处理的对象是由单个c文件和其中递归包含的头文件组成的编译单元,一般来说,头文件是不直接参加编译的。编译器会将每个编译单元翻译成同名的二进制代码文件,在DOS和Windows环境下,二进制代码文件的后缀名为.obj,在Unix环境下,其后缀名为.o,此时,二进制代码文件是零散的,还不是可执行二进制文件。

      错误检查大多是在编译阶段进行的,编译器主要进行语法分析,词法分析,产生目标代码并进行代码优化等处理。为全局变量和静态变量等分配内存,并检查函数是否已定义,如没有定义,是否有函数声明。函数声明通知编译器:该函数在本文件晚些时候定义,或者是在其他文件中定义。

   
    链接器将编译得到的零散的二进制代码文件组合成二进制可执行文件,主要完成下述两个工作,一是解析其他文件中函数引用或其他引用,二是解析库函数。

    举例来说,某个程序由两个c文件组成,分别为A.c、B.c,两个c文件和其中递归包含的头文件组成的两个编译单元,经过预处理和编译生成二进制代码文件A.obj和B.obj,假设A.c中调用了函数C,可函数C定义在B.c中,A.obj中实际上仅仅包括着对C函数的引用,其二进制定义代码需要从B.obj中提取,插入到A.obj的调用处,这个过程称为函数解析(resolve),由链接器完成。不仅仅是函数,变量(诸如有外部链接性的全局变量)也牵扯到解析的问题。当B.c没有定义函数C时,编译时不会产生错误,但链接时却会提示,有未解析的对象,据此可分析出问题出在编译阶段还是链接阶段。

    

猜你喜欢

转载自blog.csdn.net/u012101561/article/details/72792199