gcc编译工具生成动态库和静态库
一. 库的分类
1.1. 静态库(.a)
1.1.1. 静态库的代码在编译过程中已经被载入可执行程序,因此体积比较大。所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行。
1.2. 动态库(.so)
1.2.1. 动态库又名共享库。
1.2.2. 动态库(共享库)的代码在可执行程序运行时才载入内存,在编译过程中仅简单的引用,因此代码体积比较小。所以,即使程序编译完,库仍须保留在系统上,以供程序运行时调用
1.3. 两种区别
1.3.1. 静态库和动态库的最大区别,静态情况下,把库直接加载到程序中,而动态库链接的时候,它只是保留接口,将动态库与程序代码独立,这样就可以提高代码的可复用度,和降低程序的耦合度。
1.3.2. 静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库
1.3.3. 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在
二. GCC编译选项
2.1. GCC编译流程
b. 编译 Compiling
c. 汇编 Assembling
d. 链接 Linking
2.2. gcc总体选项列表
2.2.1. -E :只进行预编译/预处理,不做其他处理。
2.2.2. -S :只编译,不汇编,生成汇编代码“.S”。
2.2.3. -c :只编译,不链接,生成目标文件“.o”。
2.2.4. -o file:把输出文件输出到file里。
2.2.5. -g :在可执行程序中包含标准调试信息。
2.2.6. -v :打印出编译器内部编译各过程的命令行信息和编译器的版本。
2.2.7. -I dir :在头文件的搜索路径列表中添加dir目录
2.2.8. -L dir :在库文件的搜索路径列表中添加dir目录
2.2.9. -static :连接静态库(静态库也可以用动态库链接方式链接)
2.2.10. -llibrary :连接名为library的库文件(显示指定需要链接的动态库文件)
2.3. gcc告警和出错选项
1) -ansi :支持符合ANSI标准的C程序
2) -pedantic :允许发出ANSI C标准所列出的全部警告信息
3) -pedantic-error :允许发出ANSI C标准所列出的全部错误信息
4) -w :关闭所有警告
5) -Wall :允许发出gcc提供的所有有用的报警信息
6) -werror :把所有的告警信息转化为错误信息,并在告警发生时终止编译过程
2.4. gcc优化选项
gcc可以对代码进行优化,它通过编译选项“-On”来控制优化代码的生成,其中n是一个代表优化级别的整数。对于不同版本的gcc,n的取值范围不一致,比较典型的范围为0变化到2或者3。虽然优化选项可以加速代码的运行速度,但对于调试而言将是一个很大的挑战。因为代码在经过优化之后,原先在源程序中声明和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环语句也可能因为循环展开而变得到处都有。
三、GCC生成动态库和静态库
3.1. 生成动态库
3.1.1. 单个源文件/目标直接生成动态库
a. gcc -fPIC -shared xxx.c -o libxxx.so b. gcc -fPIC -shared xxx.o -o libxxx.so
3.1.2. 多个源文件/目标生成动态库
a. gcc -fPIC -shared xxx1.c xxx2.c xxx3.c -o libxxx.so b. gcc -fPIC -shared xxx1.o xxx2.o xxx3.o -o libxxx.so
3.2. 生成静态库
3.2.1. 单个源文件/目标直接生成静态库
a. ar -rc libxxx.a xxx.o(正确方法) b. ar -rc libxxx.a xxx.c (静态库可以生成;当运行连接了该静态库的可执行程序会报错:could not read symbols:Archive has no index;run ranlib to add one)
3.2.2. 多个源文件/目标生成静态库
a. ar -rc libxxx.a xxx1.o xxx2.o xxx3.o (正确方法) b. ar -rc libxxx.a xxx1.c xxx2.c xxx3.c (静态库可以生成;当运行连接了该静态库的可执行程序会报错:could not read symbols:Archive has no index;run ranlib to add one)
四. 实例
4.1. 动态链接库
a. 创建hello.so动态库
#include <stdio.h> void hello(){ printf("hello world\n"); } 编译:gcc -fPIC -shared hello.c -o libhello.so
b. hello.h头文件
void hello();
c. 链接动态库
PS:这里-L的选项是指定编译器在搜索动态库时搜索的路径,告诉编译器hello库的位置。"."意思是当前路径.
#include <stdio.h> #include "hello.h" int main(){ printf("call hello()"); hello(); } 编译:gcc main.c -L. -lhello -o main
d. 测试
编译成够后执行./main,会提示:
In function `main': main.c:(.text+0x1d): undefined reference to `hello' collect2: ld returned 1 exit status
这是因为在链接hello动态库时,编译器没有找到。
解决方法:
方法一:
sudo cp libhello.so /usr/lib/
方法二:一般来讲这只是一种临时的解决方案, 在没有权限或临时需要的时候使用,换个终端环境变量就不在了
export LD_LIBRARY_PATH=.:$export LD_LIBRARY_PATH
./main
方法三:
LD_LIBRARY_PATH=. ./main
4.2. 静态库实例
4.2.1. 文件有:main.c、hello.c、hello.h
4.2.2. 编译静态库hello.o:
gcc hello.c -o hello.o #这里没有使用-shared
4.2.3. 生成目标文件
PS:程序ar配合参数-r创建一个新库libhello.a,并将命令行中列出的文件打包入其中。这种方法,如果libhello.a已经存在,将会覆盖现在文件,否则将新创建。
ar -r libhello.a hello.o
4.2.4. 链接静态库
gcc main.c -lhello -L. -static -o main
这里的-static选项是告诉编译器,hello是静态库。
或者:
gcc main.c libhello.a -L. -o main
4.3. makefile实例
4.3.1. 生成静态库
# 1、准备工作,编译方式、目标文件名、依赖库路径的定义。 CC = g++ CFLAGS := -Wall -O3 -std=c++0x # opencv 头文件和lib路径 OPENCV_INC_ROOT = /usr/local/include/opencv OPENCV_LIB_ROOT = /usr/local/lib OBJS = GenDll.o #.o文件与.cpp文件同名 LIB = libgendll.a # 目标文件名 OPENCV_INC= -I $(OPENCV_INC_ROOT) INCLUDE_PATH = $(OPENCV_INC) LIB_PATH = -L $(OPENCV_LIB_ROOT) # 依赖的lib名称 OPENCV_LIB = -lopencv_objdetect -lopencv_core -lopencv_highgui -lopencv_imgproc all : $(LIB) # 2. 生成.o文件 %.o : %.cpp $(CC) $(CFLAGS) -c $< -o $@ $(INCLUDE_PATH) $(LIB_PATH) $(OPENCV_LIB) # 3. 生成静态库文件 $(LIB) : $(OBJS) rm -f $@ ar cr $@ $(OBJS) rm -f $(OBJS) tags : ctags -R * # 4. 删除中间过程生成的文件 clean: rm -f $(OBJS) $(TARGET) $(LIB)
4.3.2. 生成动态库
PS:第1、4步准备和收尾工作与静态库的保持一致,第2步和第3步所使用的命令稍有不同。
# 1、准备工作,编译方式、目标文件名、依赖库路径的定义。 CC = g++ CFLAGS := -Wall -O3 -std=c++0x # opencv 头文件和lib路径 OPENCV_INC_ROOT = /usr/local/include/opencv OPENCV_LIB_ROOT = /usr/local/lib OBJS = GenDll.o #.o文件与.cpp文件同名 LIB = libgendll.so # 目标文件名 OPENCV_INC= -I $(OPENCV_INC_ROOT) INCLUDE_PATH = $(OPENCV_INC) LIB_PATH = -L $(OPENCV_LIB_ROOT) # 依赖的lib名称 OPENCV_LIB = -lopencv_objdetect -lopencv_core -lopencv_highgui -lopencv_imgproc all : $(LIB) # 2. 生成.o文件 %.o : %.cpp $(CC) $(CFLAGS) -fpic -c $< -o $@ $(INCLUDE_PATH) $(LIB_PATH) $(OPENCV_LIB) # 3. 生成动态库文件 $(LIB) : $(OBJS) rm -f $@ g++ -shared -o $@ $(OBJS) rm -f $(OBJS) tags : ctags -R * # 4. 删除中间过程生成的文件 clean: rm -f $(OBJS) $(TARGET) $(LIB)
4.3.3. 动态/静态库调用
CC = g++ CFLAGS := -Wall -O3 -std=c++0x OPENCV_INC_ROOT = /usr/local/include/opencv OPENCV_LIB_ROOT = /usr/local/lib MY_ROOT = ../ OPENCV_INC= -I $(OPENCV_INC_ROOT) MY_INC = -I $(MY_ROOT) EXT_INC = $(OPENCV_INC) $(MY_INC) OPENCV_LIB_PATH = -L $(OPENCV_LIB_ROOT) MY_LIB_PATH = -L $(MY_ROOT) EXT_LIB = $(OPENCV_LIB_PATH) $(MY_LIB_PATH) OPENCV_LIB_NAME = -lopencv_objdetect -lopencv_highgui -lopencv_imgproc -lopencv_core MY_LIB_NAME = -lgendll all:test test:main.cpp $(CC) $(CFLAGS) main.cpp $(EXT_INC) $(EXT_LIB) $(MY_LIB_NAME) $(OPENCV_LIB_NAME) -o test
PS:在测试过程中,经常会报错:找不到.so文件。一种简单的解决方法如下:
在linux终端输入如下命令:
export LD_LIBRARY_PATH=/home/shaoxiaohu/lib:LD_LIBRARY_PATH:
参考文献:http://www.cnblogs.com/fnlingnzb-learner/p/8059251.html