一、简介
本文主要讲解gcc命令的参数以及动态库和静态库的制作,通过程序的编译过程运行具体的实例进行阐述。
二、编译过程
GCC即GNU Compiler Collection,编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编 (Assembly)和链接(Linking),gcc可以理解为编译管理工具,它会具体调用相关的工具进行执行,具体如下图:
2.1预处理
[root@localhost /]# gcc -E test.c -o test.i 或 gcc -E test.c
可以输出到test.i文件中存放着test.c经预处理之后的代码。打开test.i文件,看一看,就明白了。后面那条指令,是直接在命令行窗口中输出预处理后的代码. gcc的-E选项,可以让编译器在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将stdio.h 文件中的内容插入到test.c中了。 2.2编译为汇编代码(Compilation) 预处理之后,可直接对生成的test.i文件编译,生成汇编代码:
[root@localhost /]# gcc -S test.i -o test.s
gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。 2.3汇编(Assembly) 对于上一小节中生成的汇编代码文件test.s,gas汇编器负责将其编译为目标文件,如下:
[root@localhost /]# gcc -c test.s -o test.o
2.4链接(Linking) gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。 对于上一小节中生成的test.o,将其与C标准输入输出库进行连接,最终生成程序test
[root@localhost /]# gcc test.o -o test
gcc参数详解
编译参数 | 详细说明 |
---|
-o | 指定编译的?标,否则会?成的?标?件名是a.out; gcc main.c -o main | -S | 把源文件编译成汇编代码 | -E | 只执行预处理 | -include | 包含头文件,功能如同在源码的语句#include <xxx.h> | -I | (大写i)指定程序包含头文件的路径,一般用于指定第三方库的头文件。 | -L | 编译时,用于指定程序第三方库的查找路径。 | -l | 链接时,指定程序需要进行链接的库。注:一般库文件名是libxxx.so,-I指定xxx即可。如-Ixxx | -rpath | 程序执?需要指定动态库的路径,但是可以?-rpath参数在编译时指定程序运?时需要加载的库的路径。 | -D | 程序编译阶段可以定义一些宏,该方法可以让程序有选择性的运行代码。 | -0n | 这是程序的优化等级。n的范围是0-3。n越大优化等级越高,程序运行的越快。否则越慢,n==0时是关闭优化。利于程序的调试,一般程序调试阶段会关闭优化等级,发布程序会把优化等级设为-O2。-O0:不进行优化处理。-O 或 -O1:优化生成代码。-O2:进一步优化。-O3 比 -O2 更进一步优化,包括 inline 函数。 | -g | 打印程序的调试信息,如果需要使?gdb?具进?调试程序,程序编译的时候,需要加上该参数。 | -share | 编译的时候尽量使用动态库。(除非只有静态库,没有动态库) | -static | 禁止使用动态库,编译的时候只加载静态库,这会导致执行件很大。 | -w | 不生成任何的警告信息。 | -Wall | 生成所有的警告信息。 | -fpic | 使输出的对象模块可重定位地址方式行成的。 | -shared | 把对应的源文件形成对应的动态链接库。 |
四、实例讲解
动态库和静态库 静态库 当程序与静态库连接时,库中目标文件所含的所有将被程序使用的函数的机器码被copy到最终的可执行文件中。这就会导致最终生成的可执行代码量相对变多,相当于编译器将代码补充完整了,优点是这样运行起来相对就快些。 不过会有个缺点:占用磁盘和内存空间。静态库会被添加到和它连接的每个程序中,而且这些程序运行时,都会被加载到内存中。无形中又多消耗了更多的内存空间。 动态库 与共享库连接的可执行文件只包含它需要的函数的引用表,而不是所有的函数代码,只有在程序执行时, 那些需要的函数代码才被拷贝到内存中。
**优点:**这样就使可执行文件比较小,节省磁盘空间,更进一步,操作系统使用虚拟内存,使得一份共享库驻留在内存中被多个程序使用,也同时节约了内存。 **缺点:不过由于运行时要去链接库会花费一定的时间,执行速度相对会慢一些,总的来说静态库是牺牲了空间效率,换取了时间效率,共享库是牺牲了时间效率换取了空间效率,没有好与坏的区别,只看具体需要了。 另外,一个程序编好后,有时需要做一些修改和优化,如果我们要修改的刚好是库函数的话,在接口不变的前提下,使用共享库的程序只需要将共享库重新编译就可以了,而使用静态库的程序则需要将静态库重新编译好后,将程序再重新编译一遍。这也是使用过程当中的差别,以现在的项目举例,在远程更新的时候,如果只是.so动态库封装内容变化了,那么只需要更新.so即可。 动态库生成:
#同静态库一样编译成目标文件:
[root@localhost /]# gcc –c a.c b.c
#生成共享库:
[root@localhost /]# gcc –fPIC –shared –o libshared.so a.o b.o
静态库的生成:
#在程序编译时,会全部代码加载到可执行程序中。
[root@localhost /]# ar rcs libstatic.a fun.o
静态库的链接方法:
[root@localhost /]# gcc –o staticcode –L. –lstatic main.c –static(默认库在当前文件夹)
动态库的链接方法:
[root@localhost /]# gcc –o sharedcode -L. –lshared main.c(默认库在当前文件夹)
|