C程序的整个编译过程分为四个步骤,即1、预处理(Preprocessing),2、编译(Compilation),3、汇编(Assemble),4、链接(Linking)。
1 预处理
预处理主要是对#打头的宏定义(#define),头文件包含(#include),条件编译(#if #elif #else #endif),特殊模块(#line #pragma #ifndef #error #line等)和注释内容进行处理。
该过程由.c文件生成.i文件,用的gcc命令是:gcc –E,对应预处理命令是:cpp
以将源程序test.c编译成可执行文件test为例(下同),则为
gcc -E test.c -o test.i
等价于
cpp test.c -o test.i
2 编译
汇编主要是对程序进行词法分析,语法分析,代码优化处理,将C程序文件转化为汇编程序文件。
该过程由.i文件生成.S(.s)文件,用的gcc命令是:gcc -S,对应编译命令为:cc -S
即为:
gcc -S test.i -o test.S
等价于
cc -S test.i -o test.S
提示:
.s文件后续不在做预处理,直接汇编,故不能在.s文件中加预处理语句
.S文件后续还会做预处理,再是汇编,故可以在.S文件中加预处理语句
3 汇编
汇编主要是对程序进行部分优化,转化为二进制文件。
该过程由.S文件生成.o文件,用的gcc命令为:gcc -c,对应汇编命令为:as
即为:
gcc -c test.S -o test.o
等价于
as test.S -o test.o
4 链接
链接主要是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够在操作系统装入并执行的统一整体。
根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:
(1)静态链接
在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。
(2) 动态链接
在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。
对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。
链接过程是由.o或.so文件生成可执行二进制文件,用的gcc命令为:gcc ,对应的链接命令为:ld
即为:
gcc test.o -o test
等价于
ld test.o -o test
另外:
链接是将各种库文件连接到一起转换为二进制可执行文件的,在链接之前,可以使用ar命令将各种库打包到一起,成为一个库。as将多个.o文件打包成.a库文件,但是其不能内嵌,即不能将多个.a库文件在打包成一个.a库文件。
如:(将test1.o,test1.o,test3.o打包成test.a)
as test1.o test2.o test3.o -o test.a
如下错误:
as test1.a test2.a test3.a -o test.a