程序员的自我修养读书笔记——编译与链接

来源:互联网 发布:第三范式分解算法 编辑:程序博客网 时间:2024/05/21 06:53

 

      使用gcc进行编译c语言文件很简单:

      gabriel@gabriel-laptop:~$ gcc hello.c -o hello

      但是事实上,这个过程可分为4步,分别是预处理(Prepressing),编译(Compilation),汇编(Assembly)和链接(Linking),下面分别简述以下这四个过程,并辅以实例给大家以感性认识。

 

预编译

      预编译过程主要是处理源文件中#开头的预编译指令,主要规则如下

  1. 将所有的#define删除,并且展开所有的宏定义
  2. 处理所有的条件编译指令:#if #ifdef #elif #else #endif
  3. 处理#include,将被包含的文件插入到该预编译指令的位置
  4. 删除所有的注释//和/* */
  5. 添加行号和文件名标识,便于编译时产生调试用的行号信息
  6. 保留所有的#pragma,因为编译器需要

 

      下面就只预编译一个简单的程序看看,这里只使用#include引入自己定义的头文件。

       头文件hello.h如下:

 

       主文件hello.c如下:

 

      .c文件经过预编译之后将变成.i文件 .cpp文件经过预编译之后将会变为.ii文件

      预编译可使用如下指令,其中gcc中-E选项代表只进行预编译

      gabriel@gabriel-laptop:~$ gcc -E hello.c -o hello.i

      或

      gabriel@gabriel-laptop:~$ cpp hello.c > hello.i

 

查看文件hello.i


可以看到
  1. 头文件hello.h已经被包含进来,在hello.i的第五行显示了hello.h里面的内容;
  2. 而hello.h前前后后带#加数字的行则是告诉编译器这里是源文件hello.c中的哪一行;
  3. 再来对照hello.c看,其中条件编译语句已经被删去,取而代之的是条件编译语句中编译条件成立的那一句;
  4. 另外,同样是在hello.c中的预处理#define HELLO 3在hello.c中已经不见了,并且main()函数中使用HELLO定义的数组a也被3代替;
  5. 最后,原本在第八行的注释在预编译后的文件中也不再显示了。

      这样之后预编译的过程就算结束了

 

编译

      编译的过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编文件代码(属于编译原理的内容)

 

      编译之后将生成.s文件,即汇编代码的文件,继续使用上一步预编译完成的hello.i文件进行编译,使用如下命令,其中-S选项代表进行到编译

      gabriel@gabriel-laptop:~$ gcc -S hello.i -o hello.s

      同样,也可以直接从源文件进行预处理和编译

      gabriel@gabriel-laptop:~$ gcc -S hello.c -o hello.s

      另外,gcc专门有一个程序是完成以上编译加汇编两个步骤的,是位于/usr/lib/gcc/i486-linux-gnu/4.4/下的cc1(对于C++程序来说是cc1plus)

      gabriel@gabriel-laptop:~$ /usr/lib/gcc/i486-linux-gnu/4.4/cc1 hello.c

 

      可以看一下汇编出来的代码hello.s

 

 

      汇编代码就不过多解释了,呵呵

 

汇编

 

      汇编器是将汇编代码转变为机器可以执行的指令,也就是机器代码,由于每一个汇编语句几乎都对应一条机器指令,所以相对编译来说比较简单

 

      汇编之后生成的是目标文件.o,这已经是机器代码的文件了。继续使用上一步编译之后的hello.s文件进行汇编,使用如下指令,其中-c代表汇编

      gabriel@gabriel-laptop:~$ gcc -c hello.s -o hello.o

      或者直接使用汇编器as

      gabriel@gabriel-laptop:~$ as hello.s -o hello.o

 

      若要从源文件直接获得目标文件,则可以

      gabriel@gabriel-laptop:~$ gcc -c hello.c -o hello.o

 

 

 

链接

      最后是链接,链接是一个非常复杂的过程,虽然目标文件已经是机器代码了,但是仍要通过各种链接才能最终变成可执行文件,linux下的链接器是ld。本书前半部分大部分篇幅都会讨论链接,所以具体内容后文将给出。

 

 

总结一下

      一个c语言文件从源码文件编译链接成为可执行文件的整个过程如下所示

.c -> 预编译 -> .i -> 编译(cc1) -> .s -> 汇编(as) -> .o -> 链接(ld) -> 可执行文件

 

 

原创粉丝点击