动态链接库介绍
动态链接库,又称为共享链接库。采用动态链接库实现链接操作时,程序文件中哪里需要库文件的功能模块,GCC 编译器不会直接将该功能模块的代码拷贝到文件中,而是将功能模块的位置信息记录到文件中,直接生成可执行文件。这样带来的好处是可执行文件中记录的是功能模块的地址,真正的实现代码会在程序运行时被载入内存,这意味着,即便功能模块被调用多次,使用的都是同一份实现代码(这也是将动态链接库称为共享链接库的原因)。同样这也带来了缺陷,此方式生成的可执行文件无法独立运行,必须借助相应的库文件。
和使用静态链接库生成的可执行文件相比,动态链接库生成的可执行文件的体积更小,因为其内部不会被复制一堆冗余的代码。在Linux系统中动态链接库通常以.so结尾,在Windows系统中动态链接库通常以.DLL结尾。
动态链接库的创建
和前文静态链接库的例子一致,这里给出helloworld_c.h文件和helloworld_c.c文件。
helloworld_c.h文件
代码语言:javascript复制#ifndef HELLOWORLD_C_H
#define HELLOWORLD_C_H
#include <stdio.h>
#ifdef __cplusplus //使用__cplusplus宏配合extern "C"来告诉C 链接器,这是一个C接口。
extern "C"{
#endif
void Print_HelloWorld();
#ifdef __cplusplus
}
#endif
#endif //HELLOWORLD_C_H
helloworld_c.c文件
代码语言:javascript复制#include"helloworld_c.h"
void Print_HelloWorld()
{
printf("Hello World!n");
}
下面来制作动态链接库。一般也可以分为两种方式。
- 直接制作
GCC使用-shared 选项用于生成动态链接库;GCC使用-fpic(还可写成 -fPIC)选项的功能是,令 GCC 编译器生成动态链接库(多个目标文件的压缩包)时,表示各目标文件中函数、类等功能模块的地址使用相对地址,而非绝对地址。这样,无论将来链接库被加载到内存的什么位置,都可以正常使用。
代码语言:javascript复制gcc -Wall -shared -fPIC helloworld_c.c -o libhello.so
- 间接制作
首先编译生成中间文件,然后生成动态链接库。
代码语言:javascript复制gcc -Wall -fPIC -o helloworld.c
gcc -shared helloworld_c.o main.cpp -o libhello.so
-Wall在第一个命令里包含了即可,因为-Wall在编译的时候起作用,而在链接的时候没有用。所以第二个命令没有该选项。
无论上述哪种方式,都会生成名为libhello.so的文件。通常我们都会使用第一种方式。
动态链接库的使用
动态链接库的使用也可以有两种不同的命令方式。
- 直接使用当前目录下的动态链接库
gcc -Wall libhello.so main.c
- GCC使用-L和-l选项,选项的含义见静态链接库一文。
gcc -Wall -L. -lhello main.c
无论是那种方式,我们都生成了a.out文件,但是这时候我们还无法执行它。因为它缺少libhello.so文件。我们可以使用ldd命令来查看它所需要的所有动态链接库。
代码语言:javascript复制ldd a.out
可以看到libhello.so是not found,现在我们必须确保程序在运行时可以找到这个动态链接库。一般有下面几种方式。
- 将链接库文件移动到标准库目录下(例如 /usr/lib、/usr/lib64、/lib、/lib64)
- 在终端输入
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx
,其中 xxx 为动态链接库文件的绝对存储路径(此方式仅在当前终端有效,关闭终端后无效); - 修改~/.bashrc 或~/.bash_profile 文件,即在文件最后一行添加
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx
(xxx 为动态库文件的绝对存储路径)。保存之后,执行source .bashrc
指令(此方式仅对当前登陆用户有效)。
所以,正常来说,我们都会采用第一种方式。毕竟后两种方式可能不太友好。现在我把刚才生成的动态链接库复制到/lib下面去,然后尝试执行a.out文件。
差点忘了,我们再来看看这次生成的可执行文件的大小吧。