/**************************************************************************/ /*add.c*/ int add(int x, int y) { return x + y; return 0; } /*************************************************************************/ 然后add.h代码为: /*add.h*/ #ifndef _ADD_H_ #define _ADD_H_ int add(int, int); #endif /***************************************************************************/ main函数代码: /*main.c*/ #include <stdio.h> int main(void) { printf("2+3= %d\n", add(2,3)); return 0; } /**********************************************************************************/
1 静态库的编译流程
将add.c 单独的源文件编译成静态库libadd.a
gcc -c add.c //生成 add.o
ar crv libadd.a add.o // 生成静态库libadd.a
2 静态库的使用
gcc -o main main.c -I. -L. -ladd (注: -I -L -l的意思在文章后面)
也可以: gcc -o main main.c -I ./libadd.a (注:这里 ./libadd.a 可以是相对地址或者绝对地址 如:./lib/libadd.a , /home/lisi/lib/lib.a)
3 动态库的编译流程
gcc -fPIC -c add.c // 生成add.o
gcc -shared -o libadd.so add.o // 或者使用 ar crv libadd.so add.o
上面可合并成一行: gcc -fPIC -shared -o libadd.so add.c
注: -fPIC 使输出的对象模块是按照可重定位地址方式生成的(即与位置无关).
-shared 指定把对应的源文件生成对应的动态链接库库文件libstr.so文件
4 动态库的使用
4.1 隐式调用
代码编写与静态库一样,不需要包含到处函数的头文件,若主函数是C++程序(即.cpp), 则需要在main.cpp中用extern "C"{} 包含被调用的函数(add.c)的头文件(这里需要包含头文件是与.cpp和.c混合编译有关,同静态\动态库无关),用g++或者用gcc(加上一个链接的参数 -lstdc++)编译.
1 )代码编写:与静态库一样
2 ) 编译main.c 生成可执行程序(动态库隐式调用的使用)
gcc -o main main.c ./libadd.so
或者 gcc -o main main.c -L. libadd.so
(
注:以上两种情况执行./main时会出现
./main: error while loading shared libraries,cannot open shared object file: No such file or directory?
解决方案:
方法一: libadd.so放到/usr/lib 或 /lib 中去.
方法二: export LD_LIBRARY_PATH=$(pwd) 或者 export LD_LIBRARY_PATH=./ ,
可写入环境变量(http://blog.csdn.net/renwotao2009/article/details/40537161)来支持当前目录寻找动态链接库
方法三: 在/etc/ld.so.conf文件加入我们生成的库目录(只支持绝对路径),然后执行 #/sbin/ldconfig.
关于 /etc/ld.so.conf 可以参看:http://blog.csdn.net/renwotao2009/article/details/40109695第9条
)
或者将libadd.so 拷贝到目录 /user/lib 或者 /lib 中,然后执行 gcc -o main main.c libadd.so //此时不需要指定搜索路径
4.2 显示调用
4.2.1 在main.c 中增加头文件 #include <dlfcn.h>引入dlopen , dlsym , dlclose , dlerror 几个系统调用
主要介绍dlopen()系统调用
第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库.
-环境变量 LD_LIBARARY_PATH列出的用冒号间隔的所有目录
-文件/etc/ld.so.cache重找到库的列表,用 ldconfig 维护
-目录/usr/lib
-目录/lib
-当前目录
第二个参数:指定打开共享库.
-RTLD_NOW:将共享库重的所有函数加载到内存
-RTLD_LAZY:会退后共享库中的函数的加载操作,知道调用dlsym()时方加载某函数
返回值:返回动态库的句柄
#include <stdio.h> #include <dlfcn.h> int main() { int (*func)(int , int ); void *dl = dlopen("./libadd.so", RTLD_LAZY); if (dl == NULL) return -1; func = dlsym(dl, "add"); if (func == NULL) return -1; printf("2+3=%d", func(2, 3)); dlclose(dl); return 0; }
4.2.2 编译main.c生成可执行程序,动态库已创建
gcc -o main main.c -ldl // 注:使用libld.so库进行系统调用
5 动态库使用中遇到的问题
gcc编译文件时出现undefined reference to 'xxxx'的错误?
这是链接错误,不是编译错误,源程序代码本身没有问题,是你的编译时参数用的不对.你没有指定连接程序要用到的库,比如你的程序里用到了一些数学函数sqrt ,那么你就要在编译参数里指定程序要链接数学库,eg:gcc -o math math.c -lm.
6 gcc中一些参数的作用
6.1 -l 参数和 -L参数
-l 参数就是用来指定程序要链接的库, -l 参数紧接着就是库名, 那么库名跟真正的库文件名有什么关系呢?拿数学库来说,他的库名是m ,他的库文件名是 libm.so ,很容易看出,把库文件名的lib和尾.so 去掉就是库名了. 如第三方库名字叫做libtest.so 那么我们只需要把libtest.so 拷贝到 /usr/lib 或者 /usr/local/lib里,在编译时加上 -ltest 参数,我们就能用上libtest.so库了.
注意:要用libtest.so 库里的函数,我们还需要与libtest.so 配套的头文件.eg:gcc -o test test.c -ltest(libtest.so 的隐式调用)
6.2 -include 和-I参数
-include 用来包含头文件,但是一般情况下包含头文件都在源码里用#include xxx 实现, -include 参数很少用. -I参数用来指定头文件目录.
/usr/include 目录一般是不用指定的,gcc 知道去那里找,但是如果头文件不在 /usr/include 里我们就要用 -I参数指定. 比如 头文件放在 /myinclude目录里,那么便以命令行就加上-I/myinclude 参数,如果不加就会得到 "xx.h: No such file or deirectory "的错误. (注-I参数也可以用相对路径,比如当前目录,可以用 -I. 来指定). 也可以通过设置环]境变量C_INCLUDE_PATH来指定头文件目录,怎样设置可参看http://blog.csdn.net/renwotao2009/article/details/40537161
参考文章1:http://blog.csdn.net/star_xiong/article/details/17301191
参考文章2:http://blog.csdn.net/casularm/article/details/316149
参考文章3:http://blog.csdn.net/zzxzzy/article/details/6013185
额外知识点:
1 Linux静态库的命名规则
2 Linux动态库命名规则
3 动态库的版本信息
4 动态库的soname
.so + <library major version digit(s)>