C++动态库使用

news/2024/4/19 8:01:05/文章来源:https://blog.csdn.net/u010223072/article/details/131514197

个人博客地址: https://cxx001.gitee.io

前言

Windows与Linux下面的动态链接库区别

1. 文件后缀不同

Linux动态库的后缀是 .so 文件,而window则是 .dll 文件。

2. 文件格式不同

(a)Linux下是ELF格式,即Executable and Linkable Format

在ELF之下,共享库中所有的全局函数和变量在默认情况下都可以被其它模块使用,即ELF默认导出所有的全局符号

(b)Windows下面是PE格式的文件,即Portable Executable Format

DLL文件和EXE文件实际上是一个概念,都是PE格式的二进制文件。DLL需要显示地“告诉”编译器需要导出某个符号,否则编译器默认所有的符号都不导出。

3. 动态链接库的文件个数不一样

Linux的动态链接库就只有一个 .so 文件,还有与之对应的头文件,而在Windows下面的动态库有两个文件,

一个是引入库(.LIB)文件,

一个是动态库(.DLL)文件,

需要的头文件(.h)文件

(1)LIB引入库文件包含被DLL导出的函数名称和位置,对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。

(2)DLL文件包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。


Windows上动态库使用

1. 创建动态库

windows上创建动态库一般有两种方式:

  1. 使用_declspec 显示声明要导出的对象 。

  2. 和平时写程序一样,源码中不需要显示声明导出,导出放在def文件中声明。

首先介绍下使用_declspec 来创建动态库的过程:

(1)新建一个空项目或者是使用DLL模板都可以。(个人习惯用干净的空项目)

(2)修改项目属性输出类型改为dll。

(3)正常添加.h与.cpp文件,.h中要导出的函数前添加_declspec(dllexport)声明即可。

(4)重新生成,在工程目录即可生成对应的dllapi.libdllapi.dll文件。

再来看看使用def文件声明导出的方式:

(1)添加def文件

(2)def文件中声明要导出的函数

LIBRARY
EXPORTSadd

(3)重新生成,和第一种显示声明方式一样生成了.lib和.dll2个文件。

2. 使用动态库

windows上使用动态库一般有2种方式:

  • 隐式调用(IDE上设置)
  • 显示调用

下面分别介绍下详细的使用流程

隐式调用使用流程

(1)创建控制台测试工程,并建立一个依赖目录,将动态库的.h.lib放在这个目录下,同时将.dll放在exe可执行程序同级目录。

(2)配置

  1. 项目->属性->配置属性->VC++ 目录-> 在“包含目录”里添加头文件DllAPI.h所在的目录

  2. 项目->属性->配置属性->VC++ 目录-> 在“库目录”里添加依赖文件dllapi.lib所在的目录

  3. 项目->属性->配置属性->链接器->输入-> 在“附加依赖项”里添加dllapi.lib 。 你也可以在代码中添加一行设置库的链接,#pragma comment(lib, "dllapi.lib"),这样就不需要2、3配置了。

(3)测试运行

显示调用使用流程

只需要将.dll放在exe可执行程序同级目录就行了,IDE不需要额外设置。注意要使用这个dll中的方法其创建时必须要_declspec 显示导出,使用时只要这个dll文件就行了,.h和.lib不需要。

#include <iostream>
#include "Windows.h" // 动态库加载、释放等接口头文件
#include "tchar.h"   // _T头文件,设置支持Unicode编码typedef int(*Dllfun)(int, int); // 待使用接口的函数指针int main()
{// 加载动态库HINSTANCE hdll = LoadLibrary(_T("dllapi.dll"));if (hdll == NULL) {return -1;}// 获取动态库中导出的函数指针Dllfun funName = (Dllfun)GetProcAddress(hdll, "add");if (funName == NULL) {FreeLibrary(hdll);return -1;}// 调用、释放int ret = funName(1, 2);FreeLibrary(hdll);std::cout << "result = " << ret << "\n";
}

Linux上动态库使用

Linux上创建动态库很简单,不需要显示声明导出的函数,它会默认导出。
在Linux上使用动态库也有2种方式:

  • 编译器链接
  • 库文件加载

1. 编译器链接使用流程

  1. 编写源文件。
  1. 将一个或几个源文件编译链接,生成libxxx.so。
  2. 通过 -L<path> -lxxx 的gcc选项链接生成的libxxx.so。
  3. 把libxxx.so放入链接库的标准路径,或指定 LD_LIBRARY_PATH,才能运行链接了libxxx.so的程序。

(1) 编写源文件,生成so共享库

建立一个源文件:add.c,代码如下:

int add(int x, int y)
{return x + y;
}

编译生成libadd.so:

gcc -fPIC -shared -o libadd.so add.c

我们会得到libadd.so。

实际上上述过程分为编译和链接两步, -fPIC是编译选项,PIC是 Position Independent Code 的缩写,表示要生成位置无关的代码,这是动态库需要的特性; -shared是链接选项,告诉gcc生成动态库而不是可执行文件。

上述的一行命令等同于:

gcc -c -fPIC add.c
gcc -shared -o libadd.so add.o

(2)为动态库编写接口文件

#pragma once
int add(int x, int y);

(3)测试,链接动态库生成可执行文件

建立一个使用add函数的test.c,代码如下:

#include <stdio.h>
#include "add.h"int main(int argc, char *argv[])
{int ret = add(1, 2);printf("ret= %d.\n", ret);return 0;
}

gcc test.c -L. -ladd 生成a.out,其中-ladd表示要链接libadd.so
-L.表示搜索要链接的库文件时包含当前路径。

注意,如果同一目录下同时存在同名的动态库和静态库,比如 libadd.solibadd.a 都在当前路径下,
则gcc会优先链接动态库。

(4) 运行

运行 ./a.out 会得到以下的错误提示。

./a.out: error while loading shared libraries: libadd.so: cannot open shared object file: No such file or directory

找不到libadd.so,原来Linux是通过 /etc/ld.so.cache 文件搜寻要链接的动态库的。
/etc/ld.so.cache 是 ldconfig 程序读取 /etc/ld.so.conf 文件生成的。
(注意, /etc/ld.so.conf 中并不必包含 /lib/usr/libldconfig程序会自动搜索这两个目录)

如果我们把 libadd.so 所在的路径添加到 /etc/ld.so.conf 中,再以root权限运行 ldconfig 程序,更新 /etc/ld.so.cachea.out运行时,就可以找到 libadd.so

因此我们可以为a.out指定 LD_LIBRARY_PATH运行,如下:

LD_LIBRARY_PATH=. ./a.out

程序就能正常运行了。LD_LIBRARY_PATH=. 是告诉 a.out,先在当前路径寻找链接的动态库。

或者修改LD_LIBRARY_PATH环境变量,指定为当前目录也可以,如下:

export LD_LIBRARY_PATH=${pwd}:${LD_LIBRARY_PATH}

然后直接执行./a.out也可以运行了。

2. 库文件加载使用流程

像window调用库文件一样,在linux下,也有相应的API因为加载库文件而存在。它们主要是以下几个函数:

使用源码如下:

// test2.c#include <stdio.h>
#include <dlfcn.h>int main(int argc, char *argv[]){void * libm_handle = NULL;int (*add_method)(int, int);char *errorInfo;int result;// dlopen 函数还会自动解析共享库中的依赖项。这样,如果您打开了一个依赖于其他共享库的对象,它就会自动加载它们。// 函数返回一个句柄,该句柄用于后续的 API 调用libm_handle = dlopen("libadd.so", RTLD_LAZY );// 如果返回 NULL 句柄,表示无法找到对象文件,过程结束。否则的话,将会得到对象的一个句柄,可以进一步询问对象if (!libm_handle){// 如果返回 NULL 句柄,通过dlerror方法可以取得无法访问对象的原因printf("Open Error:%s.\n",dlerror());return 0;}// 使用 dlsym 函数,尝试解析新打开的对象文件中的符号。您将会得到一个有效的指向该符号的指针,或者是得到一个 NULL 并返回一个错误add_method = dlsym(libm_handle,"add");errorInfo = dlerror();// 它会在发生前面的错误时返回一个字符串,同时将其从内存中清空; 在没有错误发生时返回 NULLif (errorInfo != NULL){printf("Dlsym Error:%s.\n",errorInfo);return 0;}// 执行“cosf”方法result = (*add_method)(1, 2);printf("result = %d.\n",result);// 调用 ELF 对象中的目标函数后,通过调用 dlclose 来关闭对它的访问dlclose(libm_handle);return 0;
}

编译,运行./test2可以看到结果为3。

gcc test2.c -o test2 -ldl

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_325580.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CANDENCE :简单贴装器件封装绘制

紧接candence:常见表贴焊盘绘制举例 简单贴装器件封装绘制 以0603封装电容为例&#xff0c;绘制其封装&#xff0c;这里会用到前面绘制的电容的焊盘。 1、打开PCB editor 软件工具 2、新建一个PACKGE symbol 文件,设置好路径和名称。点击ok确认 3、设置画布尺寸、原点位置、…

Uniapp开发的开源盲盒系统源码

最近比较火的盲盒系统&#xff0c;该项目是基于uniapp开发的盲盒项目&#xff0c;有需要的朋友可以联系我&#xff0c;运营级的项目&#xff0c;本次开源的是uniapp前端模板&#xff0c;选用技术为JAVA&#xff0c;采用框架&#xff1a;spring bootmybatisvue开发。 通过node安…

行为型模式-策略模式(一)

今天就说一说设计模式中的策略模式&#xff0c;从名字来讲&#xff0c;意思就是&#xff0c;对应不同的情况&#xff0c;就有一种解决问题的办法&#xff0c;不同的情况&#xff0c;就有不同的应对方法&#xff0c;这就是策略模式&#xff0c;非常的智能化。 也可以参考菜鸟 …

【Git原理与使用】-- 远程操作

目录​​​​​​​ 理解分布式版本控制系统 远程仓库 新建远程仓库 lssue 与 Pull Request模板文件 知识铺垫 lssue 模板文件 Pull Request模板文件 克隆远程仓库 使用 HTTPS 方式 使用 SSH 方式 第一步&#xff1a;创建SSH Key 向远程仓库推送 过程梳理 实操 …

【SLAM学习】获取IMU和雷达消息并发布

本文主要记录如何将rosbag的消息进行获取并进行发布以及后续处理。 测试数据集&#xff1a; 链接: https://pan.baidu.com/s/1DthWE45V5Zhq7UUrfTt_CQ 提取码: mxvn 查看数据集bag包里面都有那些话题&#xff1a; rosbag info indoor_lab_RS.bag 可以看到包含了两个话题…

『手撕 Mybatis 源码』09 - MyBatis 插件

MyBatis插件 概述 问题&#xff1a;什么是Mybatis插件&#xff1f;有什么作用&#xff1f; Mybatis插件本质上来说就是一个拦截器&#xff0c;它体现了 JDK 动态代理和责任链设计模式的综合运用 Mybatis 中所允许拦截的方法如下 Executor 【SQL执行器】【update&#xff…

Docker服务编排之Docker Compose的使用

Docker服务编排 概念&#xff1a;按照一定的业务规则批量的管理容器 微服务架构的应用系统中一般包含很多微服务&#xff0c;一个微服务中又包含很多的实例&#xff0c;每个微服务都要手动管理&#xff0c;维护的工作量很大。 拉去镜像&#xff0c;创建多个容器&#xff0c;分…

【Vue3】学习笔记-自定义hook函数

概念 什么是hook? 本质是一个函数&#xff0c;把setup函数中使用的Composition API进行了封装。 类似于vue2.x中的mixin。(但是mixins会组件的配置项覆盖。vue3使用了自定义hooks替代mixnins&#xff0c;hooks本质上是函数&#xff0c;引入调用。) 自定义hook的优势: 复用代…

【密码学基础】半/全同态加密算法基础学习笔记

文章目录 1 半同态加密Pailliar加法同态加密Paillier加解密过程Paillier的同态性Paillier的安全性 El Gamal乘法同态加密RSA乘法同态加密 2 全同态加密BFV全同态加密BFV的编码方式BFV加解密过程BFV的安全性BFV的同态性自举Bootstrapping 3 同态加密应用场景场景1&#xff1a;安…

0基础学习VR全景平台篇 第54篇: 高级功能-皮肤

功能位置示意 一、本功能将用在哪里&#xff1f; 皮肤功能&#xff0c;摆脱传统VR全景展示样式&#xff0c;自行选择场景与全景分组的界面模板&#xff0c;从而与不同的应用行业风格相互适应&#xff0c;达到最贴切的展示效果。 是在各种风格的VR全景作品中&#xff0c;最快实…

ubuntu安装MobaXterm和WPS

文章目录 ubuntu安装MobaXtermi386 架构wine操作步骤 ubuntu安装WPS操作步骤WPS版本知识补充 ubuntu安装MobaXterm i386 架构 sudo dpkg --add-architecture i386 是一个Linux系统中的命令&#xff0c;用于添加一个新的架构&#xff08;architecture&#xff09;支持到当前系统…

netwox构造免费ARP数据包【网络工程】(保姆级图文)

目录 构造免费的 ARP 数据包。1) 构造免费的 ARP 数据包2) 使用 Wireshark 进行抓包 总结 欢迎关注 『网络工程专业』 系列&#xff0c;持续更新中 欢迎关注 『网络工程专业』 系列&#xff0c;持续更新中 温馨提示&#xff1a;对虚拟机做任何设置&#xff0c;建议都要先快照备…

IDEA中集成zookeeper的插件

IDEA中集成zookeeper的插件 一、IDEA中集成插件 搜索插件并安装&#xff1a; 安装完成&#xff0c;重启IDEA 配置zk集群 连接成功

【PortAudio】PortAudio 音频处理库Demo

1. 介绍 PortAudio是一个免费、跨平台、开源的音频I/O库。看到I/O可能就想到了文件&#xff0c;但是PortAudio操作的I/O不是文件&#xff0c;而是音频设备。它能够简化C/C的音频程序的设计实现&#xff0c;能够运行在Windows、Macintosh OS X和UNIX之上&#xff08;Linux的各种…

从零开始 Spring Boot 57:JPA中的一对多关系

从零开始 Spring Boot 57&#xff1a;JPA中的一对多关系 图源&#xff1a;简书 (jianshu.com) 在上篇文章中我们介绍了如何在 JPA 中实现实体的一对一关系&#xff0c;在关系型数据库设计中&#xff0c;除了一对一关系&#xff0c;还存在一对多关系。本篇文章介绍如何在 JPA 中…

【Python】NLP参数控制模板

前言 学过AI的都知道训练一个模型需要调整很多参数&#xff0c;为了有效的管理这些参数、不至于让代码的参数写的乱七八糟&#xff0c;有必要写一套控制参数的模板。 argparser argparser是python当中的参数解析器&#xff0c;在NLP当中主要是用来接受和使用参数的。一个使用它…

QT学习笔记:TCP客户端的实现

QT一般用来做客户端&#xff0c;我这里就简单讲一下怎么开发基于QT的TCP客户端。 1、用QtCreator创建项目 2、界面 3、.pro文件添加network QT core gui network 4、mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include &l…

SpringBoot的缓存管理

缓存是分布式系统中的重要组件&#xff0c;主要解决数据库数据的高并发访问问题。在实际开发中&#xff0c;尤其是用户 访问量较大的网站&#xff0c;为了提高服务器访问性能、减少数据库的访问压力、提高用户体验&#xff0c;使用缓存显得 尤为重要。Spring Boot对缓存提供了良…

基于单片机的盲人导航智能拐杖老人防丢防摔倒发短息定位

功能介绍 以STM32单片机作为主控系统&#xff1b; OLED液晶当前实时距离&#xff0c;安全距离&#xff0c;当前经纬度信息&#xff1b;超声波检测小于设置的安全距离&#xff0c;蜂鸣器报警提示&#xff1a;低于安全距离&#xff01;超声波检测当前障碍物距离&#xff0c;GPS进…

从零开始备战数学建模国赛之线性规划1.1

从零开始备战数学建模国赛之线性规划1.1 现在距离2023年的数学建模国赛还有不足三个月的时间&#xff0c;想与大家共同备战国赛。 这是我自己总结的一些代码和资料&#xff08;本文中的代码以及参考书籍等&#xff09;&#xff0c;放在github上供大家参考&#xff1a;https://…