完成宏替换、文件引入、以及去除空行、注释等,为下一步的编译做准备。
也就是对各种预处理命令进行处理,包括头文件的包含、宏定义的扩展、条件编译的选择等。、
经过预处理后,文件会变大很多。
# test.c 文件内存
#include <stdio.h>
int main() {
printf("hello world!\n");
return 0;
}
通过以下命令进行预处理
gcc -E test.c -o test.i
-E: 让gcc在预处理结束后停止编译,"test.i" 文件为预处理后输中的文件
-o : 指定输出文件
预处理前, test.c 70bytes, 预处理后 test.i 17.9KB
将预处理后的代码编译成汇编代码。在这个阶段中,首先要检查代码的规范性、是否有语法错误等,已确定代码实际要做的工作,在检查无误后,再把代码翻译成汇编语言。
编译程序执行时,先分析,后综合。分析,就是指此法分析、语法分析、语义分析和中间代码生成。综合,就是指代码优化和代码生成。
大多数的编译程序直接产生机器语言的目标代码,形成可执行的目标文件,也有的是先产生汇编语言一级的符号代码文件,再调用汇编程序进行 翻译和加工处理,最后产生可执行的机器语言目标文件。
通过以下命令进行编译
gcc -S test.i -o test.s
-S:让gcc在编译结束后停止编译过程,"test.s"文件为编译后生成的汇编代码。
汇编就是把编译阶段生成的 ".s" 文件转成二进制目标代码,也就是机器代码(01序列)
通过以下命令进行汇编
gcc -c test.s -o test.o
-c : 让gcc在汇编结束后停止编译过程 , "test.o" 文件为汇编后生成的机器码目标文件。
链接就是将多个目标文件以及所需的库文件链接生成可执行目标文件的过程。
通过以下命令进行链接
gcc test.o -o test
./ test
-o: 本质上是一个重命名选项。不使用 -o 选项时,默认生成的是a.out 文件。这里生成的是可执行文件test。
./text 执行后输出 hell world
静态库实际就是一些目标文件(一般以.o结尾)的集合,静态库一般以.a结尾,只用于生成可执行文件阶段。
在链接步骤中,连接器将从库文件取得的所有代码,复制到生成的可执行文件中。这种库称为静态库。其特点是可执行文件中包含了库代码的一份完整的拷贝,在编译过程中被载入程序中。缺点是多次使用就会有多份冗余拷贝,并且对程序的更新、部署、和发布带来麻烦,如果静态库更新,那么多有使用它的程序都需要重新编译、发布。
生成静态库:
#首先生成目标文件
gcc -c test.c -o test.o
#使用ar命令将目标文件打包成静态库
ar rcs libtest.a test.o
ar:creating libtest.a
#使用ar t libtest.a 查看静态库内容
ar t libtest.a
test.o
首先生成test.o目标文件
使用ar命令将test.o 打包成libtest.a 静态库
#使用静态库
# -l 指定链接库 -L 编译程序按目录寻找库文件
gcc -o main main.c -L. -ltool
#查看可执行文件依赖了哪些库
ldd main
动态库在链接阶段没有被复制到程序中,而是在程序运行时,由系统动态加载到内存中提供程序使用。
系统中只需要载入一次动态库,不同的程序可以得到内存中相同的动态库的副本,因此节省了很多内存。
生成动态库
#生成目标文件
gcc -c test.c -o test.o
#使用 -fPIC 和 -shared生成动态库
gcc -shared -fPIC -o libtest.so test.o
首先生成test.o目标文件。
使用 -shared 和 -fPIC 参数生成动态库
# 链接动态库
# 和链接静态库的命令一样, 当存在相同名字的静态库和动态库时,优先加载动态库
gcc -o main main.c -L. -ltool
# 执行可执行文件
# 不能直接 ./main ,会报错,找不到库文件
# 需要指定动态库,设置环境变量, 需要将动态库 tool所在目录,设置到环境变量中
LD_LIBRARY_PATH = ../main
载入时刻不同:
静态库在程序编译时会链接到目标文件中,程序运行时不再需要静态库,因此体积比较大。而且每次编译都需要载入静态代码,内存内存开销他。
动态库在程序编译时不会被链接到目标代码中,而是程序运行时才被载入,程序运行时需要动态库存在,因此体积比较小。而且系统中只需要载入一次动态库,不同程序可以得到内存中相同的动态库副本,因此内存开销小。