前段时间遇到这样这个面试题:没有头文件,能调用动态库里面的函数吗?
接下来我们可以一起来了解动态库有哪些使用方式——
动态库(Dynamic Link Library,简称DLL,在Windows环境下)或共享对象文件(Shared Object,简称SO,在Linux/Unix环境下)是包含可由多个程序同时使用的代码和数据的库文件。动态库的使用方式主要分为两大类:隐式调用(隐式链接)和显式调用(显式链接)。
一、隐式调用(隐式链接)
隐式调用是动态库的一种常见使用方式,它在程序编译时就确定了要使用的动态库。
1、制作动态库:
- 动态库对应的源文件test_lib.c:
//test_lib.c
#include <stdio.h>
void func_lib(void)
{
printf("hello!I am lib.\n");
return ;
}
- 动态库对应的头文件 test_lib.h:
void func_lib(void) ;
编译命令如下:
gcc test_lib.c -fPIC -shared -o libtest.so
说明: -fPIC :表示生成位置无关代码(PIC:Position Independent Code)
-shared : 表示创建生成动态共享库
2、在程序中声明和链接动态库
- 在程序中包含动态库的头文件,以便声明需要使用的函数和数据。
- 在编译程序时,通过编译器选项指定动态库的路径和名称。这通常涉及到在编译命令中添加
-L
(指定库文件搜索路径)和-l
(指定库文件名称,省略前缀lib
和后缀.so
或.dll
)选项。 - 在链接阶段,链接器会将程序中引用的动态库函数和数据链接到可执行文件中,但不会将动态库的代码和数据复制到可执行文件中,而是保留对动态库的引用。
测试文件 main.c 内容如下:
#include <stdio.h>
#include "test_lib.h"
int main(void)
{
func_lib();
return 0;
}
编译命令如下(和链接静态库命令基本一样):
gcc main.c -L. -ltest -o main
说明:编译的时候指定了libtest.so(上述编译好的动态库)
3、程序运行时加载动态库
- 当程序启动时,操作系统会根据可执行文件中的动态库引用信息,自动加载所需的动态库到内存中。
- 程序运行时,可以像调用静态库中的函数一样调用动态库中的函数。
运行测试:
export LD_LIBRARY_PATH=$PWD/:$LD_LIBRARY_PATH
./main
二、显式调用(显式链接)
显式调用允许程序在运行时根据需要动态地加载和卸载动态库,这种方式提供了更大的灵活性。
1、加载动态库
在程序运行时,使用dlopen
函数来加载动态库。
2、获取函数地址
使用 dlsym() 通过函数名获取动态库中函数的地址。
3、调用函数
通过上一步获取的函数地址(通常是函数指针)来调用动态库中的函数。
4、卸载动态库
在不再需要动态库时,使用 dlclose() 来卸载动态库,释放其占用的资源。
#include <stdio.h>
#include <dlfcn.h>
void (* pFuc)(void) = NULL; //函数指针
int main(void)
{
void *handle = dlopen("./libtest.so", RTLD_NOW); //打开动态库
if (handle == NULL)
{
printf("%s\n", dlerror());
return -1;
}
pFuc = dlsym(handle, "func_lib"); // 查找需要使用的符号
if ((pFuc != NULL) && (NULL == dlerror()) )
{
pFuc(); // 调用动态库中的函数
}
else
{
printf("get test_print FUNC failed.\n");
}
dlclose(handle); // 卸载动态库
return 0;
}
编译运行:
gcc main.c -ldl -o main
./main
可以看到,使用这种方式,不需要引用头文件,也可以使用动态库里面的函数!
此外,试了以下方式,也可以不需要引用头文件:
qq群交流:698593923
觉得有帮助的话,打赏一下呗。。