Linux下的动态库和静态库

在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文件一样使用

如何使用别人提供的静态库?

ar -rcs libtest.a test1.o test2.o
比较:g++ main.cpp test1.o test2.o -o helloworld
g++ main.cpp libtest.a -o helloworld
也就是说静态库在命令行里可以直接使用全路径
也可以使用-l选项,指定静态库:
g++ main.cpp -o helloworld -L .../.. -ltest,编译器就在上述指定的位置找到 libtest.a
如果在一个目录下同时存在静态库和动态库,那么编译器如何选择?
静态库与动态库的区别:
1)使用静态库:
最终的程序里含有test1和test2的代码,所以最终的程序在运行时不依赖于libtest.a的存在
2)使用动态库:
最终的程序里面没有相应代码,所以程序在运行时会寻找libtest.so
当目录中同时存在静态库和动态库时,比如:
 -build/ 
-libtest.a
-libtest.so
执行命令:
g++ main.cpp -o helloworld -Lbuild -ltest
查看用了哪个库?nm helloworld 或者 readef -d helloworld
发现链接器首先选择动态库进行链接。

以下两种方法可以强制使用静态库:
1)使用全路径: g++ main.cpp build/libtest.a -o helloworld
2)-static 强制所有的库都使用静态库版本
g++ main.cpp -o helloworld -static -Lbuild -ltest
这种方法的缺点是要求所有的库都必须提供静态库版本,少一个都不行
注意;centos 默认安装时不带libc.a libstdc++.a...

五.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的函数符号才适合手工加载。


好看好困啊!!!!



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值