假如有一个main.c 源文件,如何让它从源文件变成可执行文件呢? 其大致经过4个步骤:分别是预处理,编译,汇编,和链接。 1).预处理,产生.ii文件 2).编译,产生汇编文件(.s文件) 3).汇编,产生目标文件(.o或.obj文件) 4).链接,产生可执行文件(.out或.exe文件) 大致过程如图所示: 下面我们详细来说说每一步的具体过程:
预处理(产生.i文件,-E)执行代码:gcc -E main.c -o main.i 首先是源代码文件a.cpp和相关头文件预处理成一个.i文件。预处理的过程主要处理那些源代码文件中只能够以“#”开始的预处理指令。 主要规则如下: (1)将所以#define删除,并将宏定义展开。 (2)处理一些条件预编译指令如#ifndef,#ifdef,#elif,#else,#endif等。将不必要的代码过滤掉。 (3)处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。这个过程是递归进行的,因为被包含的文件可能也包含其他文件。 (4)预处理过程还会过滤掉所有注释/**/和//里面的内容。 (5)另外还会添加行号和文件名标识。 (6)最后会保留#pragma编译器指令,因为编译器需要使用它们。
#ifndef,#ifdef,#endif的作用是什么?防止重复包含头文件。 尖括号和双引号的区别是什么? #include<>,从标准库中寻找头文件。 #include"",从当前目录开始寻找头文件。
编译(产生汇编.s文件,-s)执行代码:gcc -S main.i -o main.s 编译就是将预处理的文件进行一系列的词法分析,语法分析,语义分析,以及优化后产生相应的汇编代码文件。进行相应的词法分析,语法分析,语义分析,源代码优化,代码生成和目标代码优化。如图所示:
汇编(产生目标.o文件或.obj文件,-c)执行代码:gcc -c main.s -o main.o 汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程,即生成目标文件。 对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。 目标文件由段组成,通常一个目标文件中至少有两个段: 代码段: 该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。 数据段: 主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。
UNIX环境下主要有三种类型的目标文件: 可重定位文件:其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。 共享的目标文件:这种文件存放了适合于在两种上下文里链接的代码和数据。第一种事链接程序可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个目标文件;第二种是动态链接程序将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象。 可执行文件:它包含了一个可以被操作系统创建一个进程来执行之的文件。 汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能得到,这个就是链接程序的工作了。
链接(产生可执行.out或.exe文件,-o)执行代码:gcc main.o -o main 由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。链接就是把每个源代码独立的编译,然后按照它们的要求将它们组装起来,链接主要解决的是源代码之间的相互依赖问题,链接的过程包括地址和空间的分配,符号决议,和重定位等这些步骤。 最基本的静态链接如图所示: 每个目标文件除了拥有自己的数据和二进制代码外,还拥有三个表,未解决符号表,地址重定向表,导出符号表。根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种: 1、静态链接/库 在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中,因此对应的链接方式称为静态链接。 静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。 静态库的缺点在于:浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。 2、动态链接/库 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。 不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。动态连接解决了共享的目标文件多个副本浪费磁盘和内存空间的问题。在内存中共享一个目标文件模块的好处不仅仅是节省内存,还可以减少物理页面的换入换出,亦可以增加CPU的cache hit。 动态链接也有其缺点:很常见的一个问题是,当程序所依赖的某个模块更新后,由于新的模块与旧的模块之间接口不兼容,导致原有的程序无法运行。