C++动态库使用

news2024/9/20 17:39:22

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构--字符串的朴素模式匹配算法

数据结构–字符串的朴素模式匹配算法 主串&#xff1a; \color{purple}主串&#xff1a; 主串&#xff1a; ‘嘿嘿嘿红红火火恍恍惚惚嗨皮开森猴开森 笑出猪叫 \color{red}笑出猪叫 笑出猪叫哈哈哈哈嗨森哈哈哈哈哈哈嗝’ 模式串&#xff1a; \color{purple}模式串&#xff1a…

计算机毕业论文内容参考|基于Python的城乡低保信息管理系统的设计和实现

文章目录 导文摘要课题背景国内外现状与趋势课题内容相关技术与方法介绍系统分析系统设计系统实现系统测试总结与展望1本文总结2后续工作展望导文 计算机毕业论文内容参考|基于Python的城乡低保信息管理系统的设计和实现 摘要 本文介绍了基于Python的城乡低保信息管理系统的设…

【电路原理学习笔记】第2章:电压、电流和电阻:2.2 电荷

第2章&#xff1a;电压、电流和电阻 2.2 电荷 电子是最小的带负电荷的粒子。当物质中存在过量的电子时&#xff0c;该物质就带负的电荷&#xff1b;当电子不足时&#xff0c;就带正的净电荷。电子和质子的电荷量相等&#xff0c;但极性相反。 电荷&#xff1a;电荷是由于物质…

企业电子名片小程序哪家?市面上哪一款名片小程序更好用?

市面上名片小程序很多&#xff0c;但是选择一款真正好用的功能强大的小程序名片就不是很多&#xff0c; 推荐你看看开利网络的链企来名片功能&#xff0c;不但具有人物的基础信息&#xff0c;还有云展厅可以上传企业信息展示企业&#xff0c;链接打通了活动&#xff0c;展会&am…

7DGroup性能实施项目日记9

好多天没写实施日记了&#xff0c;这段时间&#xff0c;我也有些其他事情要做&#xff0c;因为前阵子答应了写些东西&#xff0c;所以这几天晚上弄到两三点&#xff0c;终于写完了五万字的东西交了差。 这一段时间是培训的课程关键内容&#xff0c;基本都是分析的关键环节。主…

2023年6月NISP一级线上考试成绩发布

作为计算机行业的在校生&#xff0c;掌握更多专业知识&#xff0c;取得更有价值更有竞争力的证书&#xff0c;既能丰富自身专业知识的储备&#xff0c;又能增加毕业后就业应聘的砝码&#xff0c;早日行动&#xff0c;早日脱颖而出。 6月&#xff0c;恭喜以下同学喜提 中国信息…

Mac OS 配置java的环境变量

Mac OS 配置java的环境变量 下载java的jdk安装包 下载完成后&#xff0c;点击安装&#xff0c;一直下一步即可。Mac会默认安装到:/Library/Java/JavaVirtualMachines/jdk-1.8.jdk&#xff0c;commandoptionc复制路径 打开terminal&#xff0c;运行如下命令&#xff1a; 创建…

Qt6.5 LTS Windows使用VS2019编译全过程【包含静态编译】

Qt6.5 LTS Windows使用VS2019编译 目的编译主机环境编译依赖项方式1、 使用自定义.bat脚本编译编写脚本编译使用示例 方式2、使用官方configue脚本进行编译 目的 鉴于之前一直使用的是Qt5&#xff0c;现在Qt6已经出到Qt6.6了&#xff0c;弄个Qt6尝尝鲜吧&#xff0c;但是从Qt5…

某中厂面试题分享(附详细答案解析)

前言&#xff1a; 本篇文章主要记录上周某中厂面试题的知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出&#xff0c;对大佬有帮助希望可以支持下哦~ 小威在此先感谢各位小伙伴儿了&#x1f601; 以下正文开始 文章目…

【性能工程】性能比较:REST vs gRPC vs 异步通信

微服务之间的通信方式对微服务架构内的各种软件质量因素有重大影响&#xff08;有关微服务网络内通信的关键作用的更多信息&#xff09;。沟通方式会影响软件的性能和效率等功能性需求&#xff0c;以及可变性、可扩展性和可维护性等非功能性需求。因此&#xff0c;有必要考虑不…

Element-Plus select选择器-下拉组件错位bug(有高度滚动时)

1. bug重现 由于项目不便展示&#xff0c;因此在官网复现bug https://element-plus.org/zh-CN/component/select.html#基础用法 2. 调试 源码调试时发现下拉菜单是直接放在body 元素里&#xff0c;这时候希望它不要直接放在body里&#xff0c; 查阅文档看到这两个属性&#x…

直线模组怎样保养才合理?

直线模组简单来说就是自动领域中做直线来回往返运动的传动部件&#xff0c;被广泛应用于自动化领域市场当中&#xff0c;可实现二轴、三轴、龙门等多轴搭建的形式&#xff0c;也可用于水平使用、垂直移载使用&#xff0c;能满足自动化领域中大多数的用户。 至于直线模组的保养&…

基于Java+控制台+Mysql实现图书管理系统

基于Java控制台Mysql实现图书管理系统 一、系统介绍二、功能展示1.主页2.添加图书3.图书列表4.根据图书名称号查询图书信息5.根据编号删除图书信息6.根据编号编辑图书信息7.退出系统 三、数据库四、其它1.其他系统实现2.获取源码 一、系统介绍 使用控制台Mysql完成一个图书管理…

实现使用语音控制myCobot机械臂运动

基于语音识别技术的机器人手臂控制智能化尝试 介绍&#xff1a; 在电影《钢铁侠》中&#xff0c;我们看到托尼斯塔克在建造设备时与人工智能贾维斯交流。托尼向贾维斯描述了他需要的零件&#xff0c;贾维斯控制机械臂协助托尼完成任务。随着当今技术的发展&#xff0c;这种实现…

Redis内存策略

Redis内存回收 Redis之所以性能强&#xff0c;最主要的原因就是基于内存操作。然而单节点的Redis其内存大小不宜过大&#xff0c;否则会影响持久化或主从同步性能。 可以通过修改配置文件来设置Redis的最大内存&#xff1a; # 格式&#xff1a; # maxmemory <bytes> #…

[PyTorch][chapter 44][时间序列表示方法2]

前言 bag of words 技术里面除了上面我们讲的&#xff0c;还包括 word2Vec TF-IDF,Glove, co-occurrence matrix 等技术 论文总览 1 Abstract: 摘要 2 Introduction: 前人工作&#xff0c;本文目标 3 Model Architectures: LSA LDA 4 New Log-Linear model 5 Result…

STM32面试知识点总结分析

一、STM32F1和F4的区别&#xff1f; 内核不同&#xff1a;F1是Cortex-M3内核&#xff0c;F4是Cortex-M4内核&#xff1b; 主频不同&#xff1a;F1主频72MHz&#xff0c;F4主频168MHz&#xff1b; 浮点运算&#xff1a;F1无浮点运算单位&#xff0c;F4有&#xff1b; 功能性…

【推荐】win 安装 rust 1.70

目录 一、下载二、安装先决条件MinGW三、安装Rust四、配置国内镜像五、检查是否安装成功五、参考文章 一、下载 官网地址&#xff1a;https://www.rust-lang.org/zh-CN/ 二、安装先决条件MinGW win 安装 C运行环境 - MinGW 三、安装Rust 3.1首先设置安装路径和环境变量 配…

容器常用操作命令概述

容器镜像就可以说是一个“样板间”&#xff0c;把运行进程所需要的文件系统、依赖库、环境变量、启动参数等所有信息打包整合到了一起。之后镜像文件无论放在哪里&#xff0c;操作系统都能根据这个“样板间”快速重建容器&#xff0c;应用程序看到的就会是一致的运行环境了。 …

EMC学习笔记(十四)射频PCB的EMC设计(一)

射频PCB的EMC设计&#xff08;一&#xff09; 1.板材1.1 普通板材1.2 射频专用板材 2.隔离与屏蔽2.1 隔离2.2 器件布局2.3 敏感电路和强辐射电路2.4 屏蔽材料和方法2.5 屏蔽腔的尺寸 近十年来&#xff0c;移动通信飞速发展&#xff0c;在移动通信设备的设计、测试、安装和操作维…