在 《Python中扩展C语言加快执行速度的实现方法》 中提到了在linux下通过GCC命令编译生成动态链接库的方法,同时我们也可以通过 《Linux平台上部署Mongoose服务器的方法介绍》 中介绍的利用make工具解析makefile文件的编译规则来更高效率的处理多个源文件,其实万变不离其宗,makefile文件中仍然是以GCC命令调用编译器来编译源文件的,但对于开发者而言更加的简单有效。
相信大多数开发者有在使用Eclipse软件编辑代码,但Eclipse并不仅仅是一款编辑器,它是跨平台的自由集成开发环境,通过各种插件可以构建各种语言的开发环境。本文介绍在linux平台上使用Eclipse工具编译C工程生成静态和动态链接库方法。
GCC介绍
C/C++的编译器很多,在Linux平台上多数是以GCC为主。GCC全称为GNU Compiler Collection,目前可以编译的语言包括:C,C++,Objective-C,Fortran,Java和Ada。GCC是GNU公社的一个项目,是一个用于编程开发的自由编译器。最初,GCC只是一个C语言编译器,它是GNU C Compiler 的英文缩写。随着众多自由开发者的加入和GCC自身的发展,如今的GCC已经是一个包含众多语言的编译器了。
(1)gcc命令语法格式为选项和参数的结合:gcc [options] file1 file2...
常用选项包括:
-E:只进行预处理,不编译
-S:只编译,不汇编
-c:只编译、汇编,不链接
-g:包含调试信息
-I dir:在头文件的搜索路径列表中添加dir目录
-L dir:在库文件的搜索路径列表中添加dir目录
-v:打印编译器内部编译各过程的命令行信息和编译器的版本号
-o:输出成指定文件名
-O0:关闭所有优化选项
-O1:第一级别优化,使用此选项可使可执行文件更小、运行更快,并不会增加太多编译时间,可以简写为-O
-O2:第二级别优化,采用了几乎所有的优化技术,使用此选项会延长编译时间
-O3:第三级别优化,在-O2的基础上增加了产生inline函数、使用寄存器等优化技术
-Os:此选项类似于-O2,作用是优化所占用的空间,但不会进行性能优化,常用于生成最终版本
-w:忽略所有警告
-Werror:不区分警告和错误,遇到任何警告都停止编译
-Wall:开启大部分警告提示
注:gcc –o 和 gcc –c的区别。gcc –o是将.c源文件默认编译连接生成一个可执行的二进制代码(未加-c选项),输出文件名称由-o选项指定。该过程调用GCC的C编译器(ccl),汇编器(as)和链接器(ld)。而gcc –c在将源文件转化为目标代码之后结束,在这个过程中只调用了C编译器(ccl)和汇编器(as),而链接器(ld)并没有被执行,目标代码可在之后被连接到一个程序。
常用参数包括:
.c后缀文件——C语言源文件
.a后缀文件——由目标文件构成的档案库文件
.C或.cc或.cxx后缀文件——C++源文件
.h后缀文件——头文件;
.i 后缀文件——经过预处理的C源文件
.ii后缀文件——经过预处理的C++源文件
.m后缀文件——Objective-C源文件
.o后缀文件——编译后的目标文件
.s后缀文件——汇编语言源码文件
.S/.sx后缀文件——经过预编译的汇编语言源码文件
(2)通过GCC编译的预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)、链接(Linking)4个阶段来举例说明GCC命令的使用。
预处理,生成预编译文件(.i文件):预处理器根据字符#开头的命令读系统头文件.h内容,并插入到程序文本中去,得到一个以.i作为文件扩展名C程序。
编译,生成汇编代码(.s文件):gcc检查代码的规范性、语法等,以确定代码实际要做的工作,在检查无误后,gcc把代码编译成汇编代码。
汇编,生成目标文件(.o文件):把编译阶段生成的.s文件转化成二进制目标文件
链接,将多个目标代码模块连接生成一个大的目标模块。输入目标文件.o,汇集生成可执行二进制代码文件。这里会涉及到函数库的链接实现,在接下来的小节中介绍。
链接库介绍
根据链接时期的不同分为静态库和动态库两种。静态库是在链接阶段被连接至可执行文件中,因此即使库被删除了,程序依然可以成功运行。而动态库的链接是在程序执行的时候被连接的,在链接阶段仅连接函数的引用,因此程序编译完成后,需要将库保留在系统中,供程序运行时调用。C的标准库就是动态链接库,系统中所有运行的程序共享着同一个C标准库的代码段。
对于静态链接库来说,多个程序链接了同一个库,那么每一个生成的可执行文件都会有这个库的副本,虽然可以避免程序在其他系统上运行时缺少相关库文件,但是却导致了系统空间的浪费。而动态链接库直接在程序运行时被链接,程序的运行速度相比静态链接库会略有影响,却大大的节省了系统的资源。另外当动态链接库升级时无需像静态链接库那样重新编译链接其他原有的代码。
动态链接库的后缀名为.so,创建命令需要添加-fPIC标签告诉编译器相对跳转、-shared标签告诉编译器建立动态链接库。
命令gcc -shared -fPIC hello.c -o libhello.so
静态库的后缀名为.a,文件名的命名规范为libxxxx.a,lib为前缀,xxxx为lib的名称。例如:创建的静态库名为mystatic,则静态库文件名就是libmystatic.a。
关于在linux下通过GCC命令编译生成动态链接库的方法可参考《Python中扩展C语言加快执行速度的实现方法》一文,接下来本文详细介绍下通过GCC命令编译生成和连接静态库的方法。
静态链接库生成和链接
(1).c源程序编译成.o文件
gcc -c static_share.c
(2)ar命令将.o转换成.a静态库
ar rcs libmystatic.a static_share.o
ar:归档工具命令,把多个目标文件打包到一个归档文件中,每一个目标文件都是归档文件的一个成员。
r:插入目标文件到归档文件中,若归档文件中已存在此目标文件,则替换。若归档文件中不存在此目标文件,则创建。
c:创建归档文件,即静态链接库。
s:写入一个目标文件索引到归档文件中,或者更新已经存在的归档文件索引。对一个库做ar s等同于对该库做ranlib。randlib工具的作用为对静态库的符号索引表进行更新。
注:ar rcs libmystatic.a static_share.c生成.a静态库,应用于链接时会出现报错:./libmystatic.a: 无法添加符号: 归档没有索引;运行 ranlib 以添加一个collect2: error: ld returned 1 exit status。此处需要先生成.o目标文件,由.o文件生成静态库,否则无法添加符号至符号表。
(3)链接静态库
使用静态库中函数时,需要在源程序中包含静态库的头文件,而后使用GCC命令链接静态库生成可执行文件。
gcc -o test test.c -static -L. -lmystatic (-I/home/yuanxiao/static_share_test)
-L:指定静态库的查找位置,.表示静态库在当前目录下查找。Linux下库文件默认的路径为/usr/lib/
-l:指定静态库名,由于静态库的命名规则是以lib开头且以.a结尾,gcc会在静态库名前加上前缀lib,然后追加扩展名.a后得到的静态库文件名来查找静态库文件,因此如libmystatic.a的库,只写:-lmystatic即可。当多个静态库链接时每个静态库名前都要加-l。
-static:-lmystatic 链接libhello.so 或者libhello.a库文件, 当同时存在静态库和共享库时共享库文件优先,此时可以使用-static强制使用静态库
注:include头文件时< >引用的是系统指定路径内的头文件(linux下头文件默认路径为/usr/include/)," "引用的是程序目录的相对路径中的头文件,当编译器在程序目录的相对路径中找不到该头文件时会继续在类库路径里搜寻该头文件。因此有时需要-I选项指定所需要的头文件路径。
(4)运行可执行文件
静态链接库中的函数已经链接到目标文件中,因此删除静态库文件后程序仍然正常运行。
./test
Eclipse编译动态链接库
(1)在Eclipse中建立新的C Project,选择Shared Library,选择Linux GCC。
注:由于是Linux环境,选择Linux GCC时Eclipse无需额外配置,编译生成的目标文件基于X86-64。选择Cross Gcc时需要配置prefix和path选项才能编译成功。
(2)添加源文件。将源文件存于项目文件夹下,在项目上右击选择“refresh”直接更新至项目路径下
(3)C/C++Build--->Setting--->Include下添加路径信息后,GCC C Compiler下会同步显示。
注:在GCC C Linker选项卡下,可以发现手动创建动态链接库时-shared选项。
(4)点击工程Project,Build All完成编译,可以看到.so后缀文件。
注:此处有Debug和Release两种编译方式。Debug为调试版本,包含调试信息,并且不作任何优化,便于程序员调试程序。Release为发布版本,往往进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。 可以看到Debug方式编译选项为-O0,而 Release方式编译选项为-O3。
Eclipse生成和连接静态链接库
生成静态链接库时,将Project工程的Build Artifact选项配置为Static Library生成类型。其余也参考动态链接库的生成方法。