当前课程知识点:基于Linux的C++ > 第十二讲 Linux系统编程基础 > 12.11 库(二) > LinuxCPP1211
当我们需要使用动态库的时候
可以调用 dlopen() 函数
把这个动态库装载到内存里
dlopen() 这个函数
它所对应的头文件是 “dlfcn.h”
dlopen() 这个函数带两个参数
一个参数是 filename 一个参数是 flag
返回值类型是一个 void * 哑型指针
filename 表示的就是动态库的名字
flag 就是这个动态库的装载标志
典型的两个值是 RTLD_LAZY 和 RTLD_NOW
典型的两个值是 RTLD_LAZY 和 RTLD_NOW
RTLD_LAZY 就表示动态库的装载是惰性装载
RTLD_LAZY 就表示动态库的装载是惰性装载
就是需要的时候
才会把这个动态库装载到内存里
RTLD_NOW 是马上装载
立即装载的意思
就是 dlopen() 这个函数一执行完
就把这个动态库装载到内存里面了
一般情况下面
我们写一个程序要装载一个动态库
总是希望什么呢
就是希望这个动态库
在需要的时候才装载在内存里
我们并不需要一开始
就把这个程序需要使用到的所有动态库
都装载到内存里
所以我们参数往往给的是 RTLD_LAZY
返回值呢
就是装载完成以后这个动态库的句柄
相当于我们前面谈到的那个文件描述符
你比如讲
如果我有一个库名叫 “libtest.so”
我想把它装载进去
这是个动态库了
所以我要装载的话
我就调用 dlopen() 这个函数
传这个库的名字
传装载的标志 RTLD_LAZY
我就把它装载进去了
这个函数就会返回一个哑型指针
我们要用一个量
来记录这个函数的返回值
然后每次使用这个动态库里的函数的时候
然后每次使用这个动态库里的函数的时候
就可以使用这个动态库的句柄来去查询的
就可以使用这个动态库的句柄来去查询的
那么具体怎么查询动态库里边的某一个函数呢
那么具体怎么查询动态库里边的某一个函数呢
这里边就有一个查找和装载
动态库里的函数用的函数
叫 dlsym() 它带两个参数
一个参数是哑型指针 handle
一个参数是字符串 symbol
第一个参数 handle
就表示你要装载的那个动态库
返回的那个哑型指针
你不是要查一个函数吗
这个函数不是在这个动态库里吗
好吧 你就在那个动态库里查
那哪一个动态库呢
当然就用这个哑型指针表示的那个动态库
当然就用这个哑型指针表示的那个动态库
所以 handle 传过来的那个值
那个参数 就是 dlopen() 返回值
第二个呢 就是那个函数的名字
你想调用哪个函数
那么你就用这个名字去查找它
就完了 它的返回值呢
就是那个函数所对应的入口地址
所以你别看它返回值写的是一个 void *
它事实上是一个函数指针
这个地方是需要特别注意的
当动态库用完以后你不想用了
那你就可以 dlclose()
就关闭这个动态库
传递动态库的句柄给它
它就负责把这个动态库
给关闭或卸载
当在动态库的装载、函数的查询、
关闭的过程中出现了问题
就可以使用 dlerror() 这个函数来处理它的错误
就可以使用 dlerror() 这个函数来处理它的错误
我们看具体的例子
假设我有一个动态库
我想调用动态库中的某一个函数
我们设这个函数的名字是 g
那么我们怎么去装载这个函数呢
代码就在这里
定义一个哑型指针 handle
把它初始化成
dlopen( "libtest.so", RTLD_LAZY )
我们会动态装载 “libtest.so” 这个动态库
打开它 得到这个动态库的句柄
初始化给 handle
然后我们会调用 dlsym()
在这个动态库里查找函数 g
传参数 handle 传字符串 “g”
g 就是函数的名字
我们得到的就是指向 g 这个函数的函数指针
我们得到的就是指向 g 这个函数的函数指针
假设我们这个函数指针
它是这个样子 void ( *test )
就是没有参数 没有返回值
那么你就可以定义这样的一个函数指针变量 test
那么你就可以定义这样的一个函数指针变量 test
然后把它初始化成 dlsym() 的返回值
单独定义一个函数类型
然后根据函数类型定义我们的函数指针变量
然后根据函数类型定义我们的函数指针变量
再把那个函数指针变量赋值或初始化为 dlsym() 的结果也可以
再把那个函数指针变量赋值或初始化为 dlsym() 的结果也可以
有了这个函数指针
指向 g 函数的函数指针
我们就可以在 test 上
调用 g 函数 ( *test )
我们就调用那个 g 函数
通过函数指针调用 g 函数
调用完了 这库不就用完了
我们就 dlclose( handle )
就可以关闭这个动态库
动态库里的函数
都是按照这样的方式处理的
但同学们要注意
因为动态库的链接
需要使用 “-ldl” 选项
确保它能够被链接进去
还需要注意的是
动态库代码的实现
其中的很多函数 它都是 C 的格式
所以当你进行混合 C/C++ 代码编程的时候
所以当你进行混合 C/C++ 代码编程的时候
就需要特别注意
凡是那些 C 函数
都应该用 extern "C"{}的格式
把它的函数的原型、函数的实现都封装起来
把它的函数的原型、函数的实现都封装起来
表示它是一个 C 格式
而不是 C++ 的格式
为啥 就是因为在 C++ 代码下边
C 格式的函数和 C++ 格式的函数
函数签名是不一样的
因为 C 不支持函数重载
而 C++ 是支持函数重载的
支持重载的 C++ 函数签名
和不支持重载的 C 函数签名
是不一样的 所以为了保证
能够正确解析到 C 的函数
那么你的 C 的函数
必须封装在 extern "C"{}里
那么如果你本质上
写的基本上是 C 的代码
而不是 C++ 的代码
那么你也可以倒过来
把 C++ 的代码封装到 extern "C++"{}里
也可以
就是混合 C/C++ 编程的时候
需要特别注意的一个问题
就在这个地方
同学们一定要记住了
因为我们动态库里面的相当多的代码
都是 C 实现 而不是 C++ 实现
-1.1 提纲
-1.2 程序设计的基本概念
-1.3 简单C/C++程序介绍
-1.4 程序设计的基本流程
-1.5 基本语法元素
-1.6 程序设计风格
-1.7 编程实践
-第一讲 C/C++基本语法元素--编程实践提交入口
-2.1 提纲
-2.2 结构化程序设计基础
-2.3 布尔数据
-2.4 分支结构
-2.5 break语句
-2.6 循环结构
-2.7 编程实践
-第二讲 程序控制结构--编程实践提交入口
-3.1 提纲
-3.2 函数声明、调用与定义
-3.3 函数调用栈框架
-3.4 编程实践
-第三讲 函数--编程实践提交入口
-4.1 提纲
-4.2 算法概念与特征
-4.3 算法描述
-4.4 算法设计与实现
-4.5 递归算法(一)
-4.6 递归算法(二)
-4.7 容错与计算复杂度
-4.8 编程实践
-第四讲 算法--编程实践提交入口
-5.1 提纲
-5.2 库与接口
-5.3 随机数库(一)
-5.4 随机数库(二)
-5.5 作用域与生存期
-5.6 典型软件开发流程(一)
-5.7 典型软件开发流程(二)
-5.8 编程实践
-第五讲 程序组织与开发方法--编程实践提交入口
-6.1 提纲
-6.2 字符
-6.3 数组(一)
-6.4 数组(二)
-6.5 结构体
-6.6 编程实践
-第六讲 复合数据类型--编程实践提交入口
-7.1 提纲
-7.2 指针基本概念
-7.3 指针与函数
-7.4 指针与复合数据类型(一)
-7.5 指针与复合数据类型(二)
-7.6 字符串
-7.7 动态存储管理(一)
-7.8 动态存储管理(二)
-7.9 引用
-7.10 编程实践
-第七讲 指针与引用--编程实践提交入口
-8.1 提纲
-8.2 数据抽象(一)
-8.3 数据抽象(二)
-8.4 链表(一)
-8.5 链表(二)
-8.6 链表(三)
-8.7 链表(四)
-8.8 函数指针(一)
-8.9 函数指针(二)
-8.10 抽象链表(一)
-8.11 抽象链表(二)
-8.12 编程实践
-第八讲 链表与程序抽象--编程实践提交入口
-9.1 提纲
-9.2 程序抽象与面向对象
-9.3 类类型
-9.4 对象(一)
-9.5 对象(二)
-9.6 类与对象的成员(一)
-9.7 类与对象的成员(二)
-9.8 类与对象的成员(三)
-9.9 继承(一)
-9.10 继承(二)
-9.11 继承(三)
-9.12 多态(一)
-9.13 多态(二)
-9.14 编程实践
-第九讲 类与对象--编程实践提交入口
-10.1 提纲
-10.2 四则运算符重载(一)
-10.3 四则运算符重载(二)
-10.4 关系与下标操作符重载
-10.5 赋值操作符重载(一)
-10.6 赋值操作符重载(二)
-10.7 赋值操作符重载(三)
-10.8 赋值操作符重载(四)
-10.9 赋值操作符重载(五)
-10.10 流操作符重载(一)
-10.11 流操作符重载(二)
-10.12 流操作符重载(三)
-10.13 操作符重载总结
-10.14 编程实践
-第十讲 操作符重载--编程实践提交入口
-11.1 提纲
-11.2 泛型编程概览
-11.3 异常处理机制(一)
-11.4 异常处理机制(二)
-11.5 运行期型式信息(一)
-11.6 运行期型式信息(二)
-11.7 模板与型式参数化
-11.8 题外话:术语翻译
-11.9 泛型编程实践(一)
-11.10 泛型编程实践(二)
-11.11 泛型编程实践(三)
-11.12 泛型编程实践(四)
-11.13 泛型编程实践(五)
-11.14 泛型编程实践(六)
-11.15 泛型编程实践(七)
-11.16 泛型编程实践(八)
-11.17 泛型编程实践(九)
-11.18 泛型编程实践(十)
-11.19 编程实践
-第十一讲 泛型编程--编程实践提交入口
-12.1 提纲
-12.2 程序执行环境(一)
-12.3 程序执行环境(二)
-12.4 程序执行环境(三)
-12.5 程序执行环境(四)
-12.6 输入输出(一)
-12.7 输入输出(二)
-12.8 文件系统
-12.9 设备
-12.10 库(一)
-12.11 库(二)
-12.12 makefile文件(一)
-12.13 makefile文件(二)
-12.14 makefile文件(三)
-12.15 编程实践
-第十二讲 Linux系统编程基础--编程实践提交入口
-13.01 提纲
-13.02 进程基本概念
-13.03 信号
-13.04 进程管理(一)
-13.05 进程管理(二)
-13.06 进程管理(三)
-13.07 进程间通信(一)
-13.08 进程间通信(二)
-13.09 进程间通信(三)
-13.10 进程间通信(四)
-13.11 进程池
-13.12 编程实践
-第十三讲 进程编程--编程实践提交入口
-14.1 提纲
-14.2 线程基本概念
-14.3 线程管理(一)
-14.4 线程管理(二)
-14.5 线程管理(三)
-14.6 线程管理(四)
-14.7 线程同步机制(一)
-14.8 线程同步机制(二)
-14.9 C++11线程库(一)
-14.10 C++11线程库(二)
-14.11 C++11线程库(三)
-14.12 C++11线程库(四)
-14.13 C++11线程库(五)
-14.14 编程实践
-第十四讲 线程编程--编程实践提交入口
-15.1 提纲
-15.2 Internet网络协议
-15.3 套接字(一)
-15.4 套接字(二)
-15.5 编程实践
-第十五讲 网络编程--编程实践提交入口