C++动态库使用

2023-10-29

个人博客地址: 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
EXPORTS
	add

(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();// 它会在发生前面的错误时返回一个字符串,同时将其从内存中清空; 在没有错误发生时返回 NULL
	if (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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++动态库使用 的相关文章

  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • 如何在没有 Control.Invoke() 的情况下从后台线程修改控件属性

    最近 我们遇到了一些旧版 WinForms 应用程序 我们需要更新一些新功能 在专家测试该应用程序时 发现一些旧功能被破坏 无效的跨线程操作 现在 在您认为我是新手之前 我确实有一些 Windows 窗体应用程序的经验 我不是专家 但我认为
  • FFMPEG Seeking 带来音频伪影

    我正在使用 ffmpeg 实现音频解码器 在读取音频甚至搜索已经可以工作时 我无法找到一种在搜索后清除缓冲区的方法 因此当应用程序在搜索后立即开始读取音频时 我没有任何工件 avcodec flush buffers似乎对内部缓冲区没有任何
  • C# 中可空类型是什么?

    当我们必须使用nullable输入 C net 任何人都可以举例说明 可空类型 何时使用可空类型 https web archive org web http broadcast oreilly com 2010 11 understand
  • 使用 C# 在 WinRT 中获取可用磁盘空间

    DllImport kernel32 dll SetLastError true static extern bool GetDiskFreeSpaceEx string lpDirectoryName out ulong lpFreeBy
  • 写入和读取文本文件 - C# Windows 通用平台应用程序 Windows 10

    有用 但在显示任何内容之前 您必须在文本框中输入内容 我想那是因为我使用了 TextChanged 事件处理程序 如果我希望它在没有用户交互的情况下显示文本文件的内容 我应该使用哪个事件处理程序 因此 我想在按下按钮时将一些数据写入 C W
  • HttpClient 像浏览器一样请求

    当我通过 HttpClient 类调用网站 www livescore com 时 我总是收到错误 500 可能服务器阻止了来自 HttpClient 的请求 1 还有其他方法可以从网页获取html吗 2 如何设置标题来获取html内容 当
  • 如何在 Team Foundation 上强制发表有意义的签入评论?

    我有一个开发团队有一个坏习惯 他们写道poor签入评论 当我们必须在团队基础上查看文件的历史记录时 这使得它成为一场噩梦 我已经启用了变更集评论政策 这样他们甚至可以在签到时留下评论 否则他们不会 我们就团队的工作质量进行了一些讨论 他们很
  • 线程、进程和 Application.Exit()

    我的应用程序由主消息循环 GUI 和线程 Task Factory 组成 在线程中我调用一些第三方应用程序var p new Process 但是当我调用Application Exit 在消息循环中 我可以看到在线程中启动的进程仍在内存中
  • 我的 strlcpy 版本

    海湾合作委员会 4 4 4 c89 我的程序做了很多字符串处理 我不想使用 strncpy 因为它不会终止 我不能使用 strlcpy 因为它不可移植 只是几个问题 我怎样才能让我的函数正常运行 以确保它完全安全稳定 单元测试 这对于生产来
  • .NET 选项将视频文件流式传输为网络摄像头图像

    我有兴趣开发一个应用程序 它允许我从 xml 构建视频列表 包含视频标题 持续时间等 并将该列表作为我的网络摄像头流播放 这意味着 如果我要访问 ustream tv 或在实时通讯软件上激活我的网络摄像头 我的视频播放列表将注册为我的活动网
  • 网络参考共享类

    我用 Java 编写了一些 SOAP Web 服务 在 JBoss 5 1 上运行 其中两个共享一个类 AddressTO Web 服务在我的 ApplycationServer 上正确部署 一切都很顺利 直到我尝试在我的 C 客户端中使用
  • 可空属性与可空局部变量

    我对以下行为感到困惑Nullable types class TestClass public int value 0 TestClass test new TestClass Now Nullable GetUnderlyingType
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • 在Linux中使用C/C++获取机器序列号和CPU ID

    在Linux系统中如何获取机器序列号和CPU ID 示例代码受到高度赞赏 Here http lxr linux no linux v2 6 39 arch x86 include asm processor h L173Linux 内核似
  • 在 ASP.NET 中将事件冒泡为父级

    我已经说过 ASP NET 中的层次结构 page user control 1 user control 2 control 3 我想要做的是 当控件 3 它可以是任何类型的控件 我一般都想这样做 让用户用它做一些触发回发的事情时 它会向
  • 如何在 C# 中播放在线资源中的 .mp3 文件?

    我的问题与此非常相似question https stackoverflow com questions 7556672 mp3 play from stream on c sharp 我有音乐网址 网址如http site com aud
  • 如何将字符串“07:35”(HH:MM) 转换为 TimeSpan

    我想知道是否有办法将 24 小时时间格式的字符串转换为 TimeSpan 现在我有一种 旧时尚风格 string stringTime 07 35 string values stringTime Split TimeSpan ts new
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器

随机推荐

  • 史上最强多线程面试47题(含答案),建议收藏

    点击上方 Java之间 选择 置顶或者星标 你关注的就是我关心的 来源 java互联网架构 上一篇 天天吹微服务 单体应用有啥不好 金九银十快到了 即将进入找工作的高峰期 最新整理的最全多线程并发面试47题和答案总结 希望对想进BAT的同学
  • windows下docker的安装

    1 打开官网 https www docker com products docker desktop 看了一下官网这个页面是有些变化的 但是只要你认识windows这个单词 基本上下个windows版本的docker安装包是没问题的 2
  • ruoyi框架解决单个账户并发登录,限制多个浏览器或同一浏览器登录同个一账号

    ruoyi框架解决单个账户并发登录 限制多个浏览器或同一浏览器登录同个一账号 今天突然要解决限制一个账号多个浏览器登录问题 系统用的是若依框架 实现思路如下 application yml配置 这里在配置文件里面设置是否限制 如果以后不需要
  • 深入“自自顶向下,逐步求精”——面向过程程序设计方法

    文章转自 http blog csdn net sxhelijian article details 7303605 程序设计初学者常常受困于不会想问题 不知道让计算机解决这个问题该如何做 其实 程序员的一个基本功是 能够将复杂的问题分解开
  • 基于Vue实现一个有点意思的拼拼乐小游戏

    笔者去年曾写过一个类似的拼拼乐小游戏 技术栈采用自己的Xuery框架和原生javascript实现的 脚手架采用gulp来实现 为了满足对vue的需求 笔者再次使用vue生态将其重构 脚手架采用比较火的vue cli 前言 为了加深大家对v
  • 数据噪声以及去噪

    数据挖掘中的噪声简介 实际数据是数据挖掘算法的输入 它受多个组件的影响 其中 噪声的存在是关键因素 噪声是不可避免的问题 它会影响数据挖掘应用程序中经常发生错误的数据收集和数据准备过程 噪声有两个主要来源 隐式错误由测量工具引入 以及批处理
  • 【IEEE出版】工业自动化,机器人与控制工程国际会议(IARCE 2022)

    IARCE 工业自动化 机器人与控制工程国际会议 IARCE 2022 中国 成都 会议官方网站 www iarce org 会议邮箱 iarce hksra org 摘要时间 9月30日 全文截稿时间 10月7日 01 IARCE会议简介
  • 【Neo4j】第 6 章:节点重要性

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • mysql 多选数据类型_MySQL基础操作与数据类型

    目录 1 文件夹 库 增 改 查 删 2 文件 表 增 改 查 删 3 文件的一行内容增 改 查 删 4 创建表的完整语法 5 整型类型 6 补充sql mode 7 浮点型 8 字符类型 9 日期类型 10 枚举与集合类型 11 not
  • AIR103

    基础资料 基于Air103开发板 Air103 LuatOS 文档 上手 开发上手 LuatOS 文档 探讨重点 对官方社区库接口GPIO库使用及示例进行复现及分析 了解该的基本原理及操作方法 软件及工具版本 LuatOS AIR103 b
  • 定时任务@Scheduled用法及其参数讲解

    1 基本用法 Scheduled 由Spring定义 用于将方法设置为调度任务 如 方法每隔十秒钟被执行 方法在固定时间点被执行等 Scheduled fixedDelay 1000 上一个任务结束到下一个任务开始的时间间隔为固定的1秒 任
  • android获取版本号报错,Android 7.1 Industry版本有概率启动报错(无法获取EGLConfig)...

    本帖最后由 prece 于 2020 7 22 11 48 编辑 在RK3399开发板 Station P1上 运行Android 7 1 Industry版本有概率启动报如下错误 07 22 11 36 31 746 595 595 F
  • [OpenAirInterface实战-8] :OAI编译遇到的问题与解决方法汇总

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 OpenAirInterface实战 8 OAI编译遇到的问题与解决方法汇总 文火冰糖 王文兵 的博客 CSDN博客 问题类型1 ASN 1
  • Sum It Up HDU - 1258【DFS】

    Given a specified total t and a list of n integers find all distinct sums using numbers from the list that add up to t F
  • 十年资深建模师教你如何用zbrush在圆柱或曲面上雕刻花纹

    在圆柱上面或者是曲面比较大的模型上面制作花纹 如果我们直接使用笔刷雕或是用alpha拉出来 可能效果不是很好 对于这种情况 我们需要结合UV制作这样的图案 如果想更多了解游戏建模可以加入我们的游戏建模交流团1046 777 540 只有数字
  • 六、深入理解JDK1.8中HashMap哈希冲突解决方案

    导读 前面文章一 深入理解 Java集合初篇 中我们对Java的集合体系进行一个简单的分析介绍 上两篇文章二 Jdk1 7和1 8中HashMap数据结构及源码分析 三 JDK1 7和1 8HashMap数据结构及源码分析 续 中我们分别对
  • Java基础之【使用迭代器删除List中的元素】

    直接看代码以及代码中注释 iterator的remove跟list的remove区别是 迭代器会在remove后进行expectedModCount modCount操作 这样就会避免 ConcurrentModificationExcep
  • python+selenium尝试处理滑块验证

    效果如图 处理思路 1 打开滑动验证页面 这个用selenium一步一步走过去 2 将滑动验证码的整个图片保存下来 3 对图片的像素点进行分析 发现拼图处像素特征如下 1 阴影起点处rgb的第一个值为0 2 阴影处的rgb三个值相加大部分小
  • 【机器学习基础 2】matplotlib库

    目录 一 什么是matplotlib库 二 基本用法 1 绘制简单的线图 plot 函数 2 绘制散点图 scatter 函数 3 绘制条形图 bar 函数 4 绘制饼图 pie 函数 三 重要用法 1 设置样式 2 添加标签 3 设置坐标
  • C++动态库使用

    个人博客地址 https cxx001 gitee io 前言 Windows与Linux下面的动态链接库区别 1 文件后缀不同 Linux动态库的后缀是 so 文件 而window则是 dll 文件 2 文件格式不同 a Linux下是E