在windows环境下,我们通常在IDE如VS的工程中开发C++项目,对于生成和使用静态库(*.lib)与动态库(*.dll)可能都已经比较熟悉,但是,在linux环境下,则是另一套模式,对应的静态库(*.a)与动态库(*.so)的生成与使用方式是不同的。刚开始可能会不适应,但是用多了应该会习惯这种使用,因为步骤上并没有VS下配置那么繁琐。下面就分别总结下linux下生成并使用静态库与动态库的方法:(由于是C++项目,所以编译器用的g++,但是与gcc的使用是相通的)
首先是准备工作,把我们需要封装成库文件的函数的头文件与源文件写好,如下:
//myAPI.h int ADD(int a, int b); int MINUS(int a, int b);
//myAPI.cpp #include "myAPI.h" int ADD(int a, int b){ return a + b; } int MINUS(int a, int b){ return a - b; }
接下来准备一个测试用的主函数源文件:
//main.cpp #include "myAPI.h" #include <iostream> int main(){ std::cout << "1 + 1 = " << ADD(1, 1) << std::endl; std::cout << "1 - 1 = " << MINUS(1, 1) << std::endl; return 0; }
重要说明:
linux下用生成静态库的命令 ar 处理 myAPI.o 文件生成静态库文件,生成的库文件应遵循规范,及linux下库文件加“lib”前缀。
编译/链接生成时,也要注意,目标文件/库文件之间有依赖关系,则需要把被依赖文件放到后面(g++6.x),才能编译,
不然可能报错 找不到变量函数/ 未定义的引用 "undefined reference to".
贴上Makefile,有注解:
all: main-1 main-2 main-a main-so main-so2 main-1: myAPI.cpp main.cpp myAPI.h g++ -o main-1 myAPI.cpp main.cpp @echo "main-1 done. 直接编译省略显示编译.o文件" @echo main-2: main.cpp myAPI.o g++ -o main-2 myAPI.o main.cpp @echo "main-2 done. 显示编译.o文件" @echo main-a: libmyAPI.a g++ -o main-a main.cpp libmyAPI.a @echo "main-a done. 使用.a静态库文件 链接生成程序" @echo main-so: libmyAPI.so g++ -o main-so main.cpp ./libmyAPI.so @echo "main-so done. 直接使用.so动态库文件(需要带路径,运行时直接使用此路径) 链接生成程序" @echo main-so2: libmyAPI.so g++ -o main-so2 main.cpp -L. -lmyAPI @echo "main-so2 done. 让g++自动在当前目录("."表示当前目录,或"./")查找.so动态库文件 链接生成程序" @echo " 但运行时默认到/usr/lib目录查找,,或运行前设置环境变量 LD_LIBRARY_PATH 为动态库的路径" @echo myAPI.o: myAPI.cpp myAPI.h g++ -c myAPI.cpp @echo "myAPI.o done. 编译.o文件" @echo libmyAPI.a: myAPI.o ar crv libmyAPI.a myAPI.o @echo "libmyAPI.a done. 编译.a静态库文件" @echo libmyAPI.so: g++ -fPIC -c myAPI.cpp g++ -shared -o libmyAPI.so myAPI.o @echo "libmyAPI.so done. 编译.so动态库文件,需要 .o文件编译时加选项 -fPIC" @echo " 或者 直接编译动态库:" @echo " g++ -shared -fPIC -o libmyAPI.so myAPI.cpp" @echo #或者 直接编译动态库: #libmyAPI.so: # g++ -shared -fPIC -o libmyAPI.so myAPI.cpp # clean: rm -f *.o *.a *.so main-*
最后运行程序:
[root@lzp test2]# ./main-1 1 + 1 = 2 1 - 1 = 0 [root@lzp test2]# ./main-2 1 + 1 = 2 1 - 1 = 0 [root@lzp test2]# ./main-a 1 + 1 = 2 1 - 1 = 0 [root@lzp test2]# ./main-so 1 + 1 = 2 1 - 1 = 0 [root@lzp test2]# ./main-so2 ./main-so2: error while loading shared libraries: libmyAPI.so: cannot open shared object file: No such file or directory
可以看到 main-so直接运行了,但是 main-so2 运行出错了,找不到动态库,需要把动态库放到/usr/lib目录; 或者使用"LD_LIBRARY_PATH"环境变量后可以直接运行
[root@lzp test2]# LD_LIBRARY_PATH=./ [root@lzp test2]# ./main-so2 ./main-so2: error while loading shared libraries: libmyAPI.so: cannot open shared object file: No such file or directory [root@lzp test2]# LD_LIBRARY_PATH=./ ./main-so2 1 + 1 = 2 1 - 1 = 0
PS:但是在 G++ 6.x版本下直接运行main-so2不出错,,以上是G++4.X版本编译的
在项目开发过层中尽量让lib是垂直关系,避免循环依赖;越是底层的库,越是往后面写!
例如:
g++ ... obj($?) -l(上层逻辑lib) -l(中间封装lib) -l(基础lib) -l(系统lib) -o $@
这样写可以避免很多问题,这个是在搭建项目的构建环境的过程中需要考虑 清楚地,在编译和链接上浪费太多的生命不值得!
推荐一本书,写的很深刻:《程序员的自我修养——链接、装载与库》
[参考: http://www.tuicool.com/articles/m67z2u2; http://blog.chinaunix.net/uid-24352482-id-3199452.html]
[参考: http://www.cnblogs.com/little-ant/p/3398885.html]