静态库与动态库原理,编译流程详解

编译流程

示例代码 #test.c文件内容


#include 
int main(){
	printf("hello world!\n");
	return 0;
}

预处理(Preprocessing)

完成宏替换、文件引入、以及去除空行、注释等,为下一步的编译做准备。也就是对各种预处理命令进行处理,包括文件的包含、宏定义的扩展、条件编译的选择等。

$ gcc -E test.c -o test.i

选项-E让gcc在预处理结束后停止编译,“test.i”文件为预处理后输出文件。-o 指定输出文件
预处理后文件会变大很多。

编译(Compilation)

1将预处理后的代码变成汇编代码。在这个阶段中,首先要检查代码的规范性、是否有语法错误等,以确定代码实际要做的工作,在检查无误后,再把代码翻译成汇编语言。
2.编译程序执行时,先分析,后综合。分析,就是指此法分析、语法分析、语义分析和中间代码生成。综合,就是指代码优化和代码生成。
3.大多数的编译程序直接产生机器语言的目标代码,形成可执行的目标文件,也有的是先产生汇编语言一级的符号代码文件,再调用汇编程序进行翻译和加工处理,最后产生可执行的机器语言目标文件。

$ gcc -S test.i -o test.s

选项-S让gcc在编译结束后停止编译过程,“test.s”文件为编译后生成的汇编代码。
2

汇编(Assemble)

汇编就是把编译阶段生成的“.s”文件转成二进制目标代码,也就是机器代码(01序列)。

$ gcc -c test.s -o test.o

选项-c让gcc在汇编结束后停止编译过程,“test.o”文件为汇编后生成的机器码目标文件。

链接(Linking)

链接就是将多个目标文件以及所需的库文件链接生成可执行目标文件的过程。

$ gcc test.o -o test
$ ./test
hello world

-o本质上是一个重命名选项。不适用-o选项时,默认生成的是a.out文件。这里生成的是可执行文件test。

静态库

1.静态库实际就是一些目标文件(一般以.o结尾)的集合,静态库一般以.a结尾,只用于生成可执行文件阶段。
2.在链接步骤中,链接器将从库文件取得所需代码,复制到生成的可执行文件中。这种库成为静态库。其缺点就是可执行文件中包含了库代码的一份完整拷贝,在编译过程中被载入程序中。缺点就是多次使用就会有多份冗余拷贝,并且对程序的更新、部署和发布会带来麻烦,如果静态库有更新,那么所有使用它的程序都需要重新编译、发布。

如何生成静态库:
首先生成test.o目标文件:

$ gcc -c test.c -o test.o

使用ar命令将test.o打包成libtest.a静态库

$ ar rcs libtest.a test.o

使用ar t libtest.a 查看静态库内容

$ ar t libtest.a 
test.o

如果生成的静态库中不包含main函数则需要链接静态库到main函数:

$ gcc -o main main.c -L. -ltest

main.c为主函数,test为上述的test.o 生成的可执行文件可以直接运行。
-l:指定要链接的静态库
-L:编译程序按照-L指定的路径去寻找库文件. 表示在当前文件去找

$ ldd main

查看可执行文件依赖

动态库

1.动态库在链接阶段没有被复制到程序中,而是在程序运行时由系统动态加载到内存中供程序调用。
2.系统只需要载入一次动态库,不同的程序可以得到内存中相同动态库的副本,因此节省了很多内存。

如何生成动态库:
首先生成test.o目标文件。

$ gcc -c test.c 

使用-share和-fPIC参数生成动态库。

$ gcc -shared -fPIC -o libtest.so test.o

编译可执行文件并链接动态库

$ gcc -o main main.c -L. -ltest

此时生成的可执行文件无法直接使用,需要动态设置动态库地址。

$ LD_LIBRARY_PATH. ./main

把动态库地址设置到当前目录下面即可。

静态库与动态库区别

1.静态库在程序编译时会链接到目标代码中,程序运行时不需要静态库,因为体积比较大。而且每次编译时都需要载入静态代码,因此开销大。
2.动态库在程序编译时不会被链接到目标代码中,而是在程序运行时才被载入,程序运行时需要动态库存在,因此体积比较小。而且系统只需要载入一次动态库,不同程序可以的到内存中相同的动态库副本,因此内存开销小。

你可能感兴趣的:(ndk,android,c基础)