Linux下程序编译链接过程
在Linux下使用gcc将源码(.c文件)编译成可执行文件的过程可以分解为4个步骤,分别是预处理(Prepressing),编译(Compilation),汇编(Assembly),链接(Linking)
1.预处理
.c ----> .i
命令:gcc -E hello.c -o hello.i
---> -E:可以使编译器在预处理完成后就停止编译 -o:指定gcc输出的结果
预处理过程主要处理那些源代码中以#开头的代码:
1.处理#define,展开所有的宏定义
2.处理条件编译指令,如#ifdef、#else、#endif
3.处理#include,将包含的头文件插入到该预处理指令的位置,该过程递归进行,因为被包含的文件可能还包含其他文件
4.添加行号和文件标识,如#2 “hello.c” 2,以便编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号信息
5.保留所有的#pragma编译器指令,因为编译器要使用它们
2.编译
.i ----> .s
命令:gcc -S hello.i -o hello.s 或者 gcc -S hello.c -o hello.s
---> -S:可以使编译器在编译完成后就停止编译 -o:指定gcc输出的结果
编译过程就是把预处理完的文件进行一系列词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件(.s)
1.语法分析:分析表达式是否遵循语法规则
2.词法分析:分析关键字,标识符,立即数是否合法
3.语义分析:在语法分析基础上进一步分析表达式是否合法
3.汇编
.s ----> .o
命令:gcc -c hello.s -o hello.o 或者 gcc -c hello.c -o hello.o
---> -c:可以使编译器在汇编完成后就停止编译 -o:指定gcc输出的结果
汇编就是将汇编代码转变成机器可以执行的命令,生成目标文件(.o),汇编器as根据汇编指令和机器指令的对照表一一翻译即可完成
4.链接
多个.o ----> 可执行文件
命令:gcc -o hello hello.c //一步到位
链接就是链接器ld将各个目标文件组装在一起,解决符号依赖,库依赖关系,并生成可执行文件
同时,链接处理可以分为两种:静态链接,动态链接
在这里涉及一个很重要的概念,函数库。
大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。
举个列子:我们常用的printf函数,一般我们只会包含一个头文件(#include <stdio.h>),但这其中只有该函数的声明,而没有函数的具体实现,那么该函数的实现在哪里呢?------->答案是函数库,这里是libc.so.6 库
5.运行
./hello //当编译通过后,执行./+可执行文件名就可以运行文件了
其实可以把gcc当成一个翻译组织,它有很多成员(翻译官)。他根据不同的参数去调用不同的翻译官来翻译。最后,了解这四个过程中所做的工作,对我们理解头文件、库等的工作过程是有帮助的,而且清楚了编译链接过程还对我们在编程时定位错误,以及编程时尽量调动编译器的检测错误也有很大帮助。