库:
在c/c++里,使用库(library)的技术,可以将编译好的符号提供给第三方使用。
1)共享库(动态库)share library
2) 静态库 static library
一:动态库的生成办法:
使用g++命令来生成动态库
编译,生成.o文件(编译选项:-fPIC)
g++ -c -fPIC example.cpp -o example.o
链接,生成目标.so文件,(链接选项:-shared)
g++ -shared example.o -o libexample.so
注:PIC:position independent code 位置无关代码
规范命令:前缀:lib 后缀: .so
使用nm命令查看库中的符号:
nm libexample.so
交付物:
example.h+libexample.so,同时告知.so文件适用的平台。
二:动态库的使用
包含头文件,调用其中的函数,编译+链接:g++ main.cpp -o helloworld -L. -lexample
链接选项:
-lexample 使用libexample.so这个库文件
-L.指定库文件的位置(当前路径) ,-L+路径 比如-L/opt
那么当.so文件就在当前目录下的时候程序仍然提示找不到库文件怎么办?
注意:操作系统默认从标准位置寻找相应的库
/lib /usr/lib/ /usr/local/lib
如果没有找到依赖的库文件,则从LD_LIBRARY_PATH环境变量里寻找
也就是说,库文件要么放在标准位置,要么放在LD_LIBRARY_PATH指定的位置,才能被操作系统找到
设置环境变量:使用export命令设置环境变量,然后再运行程序
export LD_LIBRARY_PATH=.
./helloworld
可以使用echo查看环境变量里面的内容 :echo $LD_LIBRARY_PATH
那么拿到一个可执行程序,怎么知道它依赖哪些库呢?
readelf -d helloworld
我们至少要知道两个基本的库:libc.so标准c库
libstdc++.s标准c++库(包含STL)
不需要在命令行中指定,g++默认会链接到这两个库。
补充:
1)在Makefile里面生成动态库
增加编译项:-fPIC 增加链接项:-shared
改traget为libexample.so
编译:%.o:%.cpp:
g++ -c -fPIC -MMD $< -o $@
2)在创建动态库时,里面可以调用其他的动态库,如果需要调用其他第三方库,可以在链接时添加链接选项:
g++ -shared $(Objects) -o $(EXE) -lxxx(xxx为需要链接的库名)
3)动态库共享class类
引用头文件,建立此类的对象,调用此类的函数
三.库的目录结构
linux下面可能会经常使用各种库,有的是系统自带的库,有的是第三方库
通常它们的目录是:libxxx/
-lib/
-include/
其中bin下为程序,lib下为库文件,include为头文件
系统自带库放在:
/usr
-include/
-lib/
如何使用第三方库,
头文件:
使用编译选项-l 参数来指定,修改Makefile(编译选项和链接选项)
g++ -c -l /home/mytest/example/include...指定头文件的位置
库文件引用:
-L /home/mytest/example -lexample或者-I/home/mytest/example/libexample.so全路径指明库文件的位置
通常在Makefile里定义两个变量,CXXFLAGS表示c++编译选项,LDFLAGS表示链接选项
INCLUDE路径:用-I选项指定自定义的INCLUDE路径
标准INCLUDE路径:/usr/include /usr/local/include
四.静态库的创建与使用
静态库,static library
标准命名:libxxx.a
第一步:编译,得到*.o文件
第二步:打包
ar -rcs libxxx.a file1.o file2.o ...fileN.o
注意:sr只是将*.o文件打个包而已,并非链接
静态库的交付物:
-头文件*.h
-库文件libxxx.a
另外需要说明静态库适用的平台
静态库的本质就是将一些.o文件打个包而已,使其可以像.o文件一样使用
如何使用别人提供的静态库?
-build/
五.C的函数与C++的函数的区别
1)按照c++编译example.cpp
g++ -fPIC -shared example.cpp -o libexample.so
nm libexample.so
结果是:.....T _Z7exampleii
2)按照c编译example.c
gcc -fPIC -shared example.c -o libexample.so
nm libexample.so
结果是:.....T example
由于c函数生成的目标文件名称就是函数名,因此不能重名,但是c++可以
那么c++中如何使用c的库呢?
比如,linexample.so是一个c的库,在main.cpp中声明头文件,调用函数example(),执行命令:
g++ main.cpp -o helloworld -Lbuild -lexample,会出现undefined reference 的错误。
原因是:编译器要找的是_Z7exampleii,而libexample.so里面的符号是example,所以会报告出错。
解决办法:
如果在c++里需要调用一个c的函数符号,需要添加extern "C"
例如在mian.cpp里面调用时用以下语句:extern "C" vodi example(int a, int b)
extern "C":
1)声明单个函数,如上
2)声明多个函数,
extern “C”
{
void fun1()
void fun2()
}
一般情况下,应该由库的作者提供一个兼容c/c++的头文件
比如example.h
/example.h
#ifdef __cplusplus
extern "C"
{
#endif
void example(int a ,int b);
#ifdef __cplusplus
}
#endif
这个是一个c/c++都兼容的头文件
也可以这样使用:
extern "C"
{
#include "example.h"
}
六.动态库的手工加载
手工加载,在程序运行时刻,根据实际需要来加载、卸载
使用dl库中的函数(dynamic linking loader)
头文件:
#include<unistd.h>
#include<dlfcn.h>
链接选项:
-ldl
g++ main.cpp -o helloworld -ldl
代码里面使用的dl函数:
1)加载:dlopen
2)找到函数符号:dlsym
3)调用函数(函数指针的语法)
4)卸载:dlclose
具体的代码如下:
//main.cpp
#include<stdio.h>
#include<unistd.h>
#include<dlfcn.h>
int main()
{
//加载库
void *lib = dlopen("build/libexample.so",RTLD_NOW);
if(!lib)
{
printf("failed to load library!\n");
return -1;
}
//寻找函符号
typedef void (*SO_FUNCTION)(int ,int);
SO_FUCNTION f = (SO_FUNCTION) dlsym(lib, "example");
if(!f)
{
printf("failed to find the function!\n");
return -1;
}
//调用函数
f(1,2);
//卸载动态库
dlclose(lib);
return 0;
}
常见问题:1)dlopen失败?
检查文件路径
2)dlsym失败
用nm命令查看目标so是否有该符号。
注意:只有c的函数符号才适合手工加载。
好看好困啊!!!!