编译流程
编译分为四大步骤:
- 预处理
- 编译
- 汇编
- 链接
预处理
●完成宏替换、文件引入,以及去除空行、注释等,为下一步的编译做准备。
●也就是对各种预处理命令进行处理,包括头文件的包含、宏定义的扩展、条件编译的选择等。
指令:
# 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
:指定输出文件。
![](https://img.haomeiwen.com/i11952059/95a80a74ac4353e8.png)
我们可以看到处理之后的文件的体积会变大很多,而且“test.i”为文本文件
,可以用文本编辑器打开
编译
-
将预处理后的代码编译成汇编代码。在这个阶段中,首先要检查代码的规范性、
是否有语法错误等,以确定代码实际要做的工作,在检查无误后,再把代码翻译
成汇编语言。 -
编译程序执行时,先分析,后综合。分析,就是指词法分析、语法分析、语义分
析和中间代码生成。综合,就是指代码优化和代码生成。 -
大多数的编译程序直接产生机器语言的目标代码,形成可执行的目标文件,也有
的是先产生汇编语言一级的符号代码文件,再调用汇编程序进行翻译和加工处理,
最后产生可执行的机器语言目标文件。
gcc命令(对预编译后的文件执行编译指令):
$ gcc -S test.i -o test.s
选项-S让gcc在编译结束后停止编译过程,“test.s”文件为编译后生成的汇编代码。
汇编
- 汇编就是把编译阶段生成的“.s” 文件转成二进制目标代码,也就是机器代码(01序列)。
gcc命令(对编译后的文件执行汇编指令):
$ gcc -C test.s -o test.o
选项-c让gcc在汇编结束后停止编译过程,“test.o”文件为汇编后生成的机器码目标文件。
链接
- 链接就是将多个目标文件以及所需的库文件链接生成可执行目标文件的过程。
gcc命令:
$ gcc test.o -o test
-o本质上是一个重命名选项。不使用-o选项时,默认生成的是a.out文件。这里生成的是可执行文件test
./test
执行后输出"helloworld!"
或者我们可以这一步到位,用gcc命令直接将.c文件生成一个可执行文件
gcc命令:
gcc -o test test.c
静态库与动态库
了解了编译流程之后,我们看下静态库和动态库
静态库
静态库概念:
-
静态库实际就是一些目标文件(一般以.o结尾)的集合,静态库一般以.a结尾,只用于生成可执行文件阶段
在可执行文件执行时,不需要依赖静态库
。 -
在链接步骤中,链接器将从库文件取得所需代码,复制到生成的可执行文件中。这种库称为静态库。其特点是可执行文件中包含了库代码的一份完整拷贝,在编译过程中被载入程序中。缺点就是多次使用就会有多份冗余拷贝,并且对程序的更新、部署和发布会带来麻烦,如果静态库有更新,那么所有使用它的程序都需要重新编译、发布。
生成静态库的gcc命令:
#首先生成目标文件(如果需要,生成多个目标文件)
//$ gcc -C test.c与下面一行代码等效,会生成一个同名.o文件,也就是test.o
$ gcc -C test.c -o test.o
#使用ar命令将目标文件打包成静态库 选项rcs分别表示
#r表示更新或增加新文件
#c表示创建一个库,不管存在与否
#s表示创建文档索引
$ar rcs libtest.a test.o
//ar: creating libtest.a
#使用ar t libtest.a 查看静态库内容
$ ar t libtest. a
test.o
动态库:
-
动态库在链接阶段没有被复制到程序中,而是在程序运行时由系统动态加载到内存中供程序调用。
-
系统只需载入一次动态库,不同的程序可以得到内存中相同动态库的副本,因此节省了很多内存。
生成动态库的gcc命令:
#首先生成目标文件(如果需要,生成多个目标文件)
//$ gcc -C test.c与下面一行代码等效,会生成一个同名.o文件,也就是test.o
$ gcc -C test.c -o test.o
#使用- fPIC和- shared生成动态库
$ gcc -shared -fPIC -o libtest.so test.o
其中fPIC
表示:用于生成位置无关的代码(Position-Independent-Code),以供生成动态库使用
注:生成静态库或动态库后,可通过gcc命令编译生成可执行文件:
//生成静/动态库后
//编译可执行文件,并且链接静态库,
//-l表示指定要链接的库后面跟的是库的名字(这里是:tool),
//-L表示编译程序暗转-L后面指定的路径去寻找库文件 . 表示当前文件夹
//$gcc -o main main.c -L. -ltool
使用上述命令需要注意:
- 如果目标文件夹下存在静态库和动态库重名,gcc优先使用动态库
- 动态库生成的可执行文件不能直接通过
./xxx
执行,需要在执行之前设置环境变量#LD_LIBRARY_PATH=. . /main
,该环境变量主要用于指定查找共享库,也就是动态链接库时除了默认路径以外的其它路径
![](https://img.haomeiwen.com/i11952059/7199fe3d5cbcbeba.png)
动态库与静态库的区别:
载入时刻不同:
● 静态库在程序编译时会链接到目标代码中,程序运行时不再需要静态库,因此体积较大。而且每次编译都需要载入静态代码,因此内存开销大。
● 动态库在程序编译时不会被链接到目标代码中,而是在程序运行时才被载入,程序运行时需要动态库存在,因此体积较小。而且系统只需载入一次动态库,不同程序可以得到内存中相同的动态库副本,因此内存开销小。
网友评论