待补充: GitHub上传代码。
验证结论
不同动态库中含有同名函数windows与Linux下均不会发生冲突。
预备知识
静态库和动态库本质还是程序。在项目中使用库一般有两个目的,一个是为了使程序更加简洁,不需要在项目中维护太多的源文件,另一方面是为了源代码保密,毕竟不是所有人都想把自己编写的程序开源出来。
当我们拿到了库文件(动态库、静态库)之后要想使用还必须有这些库中提供的 API 函数的声明,也就是头文件,把这些都添加到项目中,就可以快乐的写代码了。
静态库
(1)编译时把静态库中的相关代码复制到可执行文件中,运行时不需要链接库
(2)程序运行时无需加载库,运行速度快(优点)
(3)占用更多的磁盘和内存空间,静态库升级后,程序需要重新编译,库升级不方便(缺点)
后缀:均以.lib开头。Windows下静态库以.lib 结尾。Linux下以.a结尾。
windows静态库创建方式:
使用VS创建时可在选择空项目时直接选择静态库或动态库,也可在创建好后进行修改。
Linux静态库创建方式:
生成静态库,需要先对源文件进行汇编操作 (使用参数 -c) 得到二进制格式的目标文件 (.o 格式), 然后在通过 ar 工具将目标文件打包就可以得到静态库文件了 (libxxx.a)。
使用 ar 工具创建静态库的时候需要三个参数:
参数c:创建一个库,不管库是否存在,都将创建。
参数s:创建目标文件索引,这在创建较大的库时能加快时间。
参数r:在库中插入模块 (替换)。默认新的成员添加在库的结尾处,如果模块名已经在库中存在,则替换同名的模块。
步骤一:
在系统提示符下键入以下命令得到hello.o文件。
gcc -c hello.c
Linux下静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为 hello,则静态库文件名就是 libhello.a 。
步骤二:
现在有了.o文件,接下来对其进行打包。创建静态库用 ar 命令。在系统提示符下键入以下命令将创建静态库文件 libhello.a 。
ar -rcs libmyhello.a hello.o //可接多个.o文件
3.将生成的的静态库 libmyhello.a 和库对应的头文件 head.h 一并发布给使用者就可以了。
使用静态库:
将静态库连接编译并运行,在编译的时将静态库的路径和名字都指定出来。
-L: 指定库所在的目录 (相对或者绝对路径)
-l: 指定库的名字,需要掐头 (lib) 去尾 (.a) 剩下的才是需要的静态库的名字
gcc -o hello main.c -L. -l myhello
./hello
-o 表示指定输出文件名字,如果不指定则根据.c文件生成默认命名,此处指定为hello。
-main.c 表示要使用静态库的程序文件。
-L 指定第三方库目录, 此处-L.表示在当前目录查找第三方库。
-l myhello(掐头去尾,与-l之间有无空格均可) 表示连接名为libhello.a或libhello.so的库。
动态库
(1)编译时仅记录使用哪个共享库(动态库)中的哪个符号(函数),不复制共享库中的相关代码,运行时加载共享库。
(2)程序不包含库中的代码,代码尺寸小(优点)。
(3)库升级方便,无需重新编译(优点)。
(4)使用更广泛
后缀:均以.lib开头。在win系统下后缀为.dll,在Linux下后缀为.so。
windows中使用的制作工具不同,得到的库文件也不同。注意:动态库有执行权限,静态库没有。
windows动态库创建方式:使用VS创建时可在选择空项目时直接选择静态库或动态库,也可在创建好后进行修改。
Linux动态库创建方式**:
生成动态链接库是直接使用 gcc 命令并且需要添加 -fPIC(-fpic) 以及 -shared 参数。
-fPIC 或 -fpic 参数的作用是使得 gcc 生成的代码是与位置无关的,也就是使用相对位置。
-shared参数的作用是告诉编译器生成一个动态链接库(无位置要求)。
步骤一:
无论静态库还是动态库,都是由.o文件创建的。因此,必须将静态库的.c文件通过gcc先编译成.o文件,在系统提示符下键入以下命令得到hello.o文件。
gcc -c hello.c -fpic
步骤二:
Linux下动态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.so。例如:我们将创建的静态库名为 hello,则动态库文件名就是 libhello.so 。
创建动态库用 -shared参数。在系统提示符下键入以下命令将创建动态库文件 libhello.so。
gcc -shared -o libhello.so hello.o,
上述两步可以连在一起:
gcc -shared -fPIC -o libhello.so hello.c
使用动态库:
生成可执行程序。
gcc -o main main.c -L . -lhello
./main
在执行使用了动态库的程序时,可能会提示加载不到动态库
Linux提供了一个动态链接器:
动态链接器是一个独立于应用程序的进程,属于操作系统,当用户的程序需要加载动态库的时候动态连接器就开始工作了,很显然动态连接器根本就不知道用户通过 gcc 编译程序的时候通过参数 -L 指定的路径。
那么动态链接器是如何搜索某一个动态库的呢,在它内部有一个默认的搜索顺序,按照优先级从高到低的顺序分别是:
1.可执行文件内部的 DT_RPATH 段
2.系统的环境变量 LD_LIBRARY_PATH //相当于操作系统提供的全局变量,里面可以存储一些路径。相当于PATH
3.系统动态库的缓存文件 /etc/ld.so.cache
4.存储动态库 / 静态库的系统目录 /lib/, /usr/lib 等
按照以上四个顺序,依次搜索,找到之后结束遍历,最终还是没找到,动态连接器就会提示动态库找不到的错误信息。
解决方案(对应上述2,3,4):
我们只需要将动态库的路径放到对应的环境变量
或者系统配置文件
中,同样也可以将动态库拷贝到系统库目录
(或者是将动态库的软链接文件放到这些系统库目录中)。
方案 1: 将库路径添加到环境变量 LD_LIBRARY_PATH 中
查看环境变量值:
echo $LD_LIBRARY_PATH // 此处$是为了取到LD_LIBRARY_PATH的值,不加的话会打印出个LD_LIBRARY_PATH
方案 2: 更新 /etc/ld.so.cache 文件
方案 3: 拷贝动态库文件到系统库目录 /lib/ 或者 /usr/lib 中 (或者将库的软链接文件放进去)
建议使用软连接,这样即使动态库中内容修改了,也不需要进行重新拷贝。
在启动可执行程序之前,或者在设置了动态库路径之后,我们可以通过一个命令检测程序能不能够通过动态链接器加载到对应的动态库
,这个命令叫做 ldd:
ldd main(可执行的程序名称)
示例
两个不同的动态库含有同名函数是否冲突验证。
步骤一:
创建两个动态库(新建项目时选择动态库选项或在项目属性中进行修改,可参考视频教程:B站教程):DLL_1,DLL_2,两动态库中包含同名函数get_info()。内容以动态链接库DLL_1为例,动态链接库2中对应修改函数中打印内容为“这是动态库2…”即可:
dll1.h中内容为:
#pragma once
#include <stdio.h>
#ifdef WIN32
#define DllExport __declspec( dllexport )
#else
#define DllExport __attribute__((visibility("default")))
#endif
DllExport
void
get_info();
dll1.c中内容为:
#include "dll1.h"
DllExport
void
get_info()
{
printf("这是动态库1:dll_1\n");
}
步骤二:
现在已经有了两个动态库,接下来编写主项目DllTest用于调用两个动态库中的内容。
注意: 其中。DLL_11与DLL_22为两个静态库,完整代码及测试结果见 github:
DllTest.h中的内容为:
DllTest.c中的内容为:
TEST4:
TEST3:
TEST2:
TEST1: