动态链接库dll和静态链接库教程

动态链接库dll和静态链接库教程

1动态库的创建
我们打开我们的vs,然后新建一个testDLL项目

在这里插入图片描述
接着我们在我们的添加一个DLL1.h文件和一个DLL1.cpp文件
DLL1.h实现内容如下`

//_declspec(dllimport)是导出函数的意思,让编译器认为这个
//是一个可以让外部访问的一个函数(API)  
//在这里我们add函数默认是_declspec(dllimport) _cdecl int add(int a, int b); 的一个方式申明的(稍后这边会详细讲解这部分

_declspec(dllexport) int add(int a, int b);

DLL.cpp实现内容:

#include "pch.h"
#include "DLL1.h"
//dll导出函数的实现
_declspec(dllexport) int add(int a, int b)
{
	return a+b;
}

好了我们生成一下我们的testDLL项目,然后打开debug目录,此时我们可以看到一个testDLL.dll文件,和一个testDLL.lib文件
在这里插入图片描述
接下来我们再来创建一个控制台应用程序,选择项目空项目,项目名称为Main,添加我们的一个main.cpp文件,以及添加我们的main函数
在这里插入图片描述
接下来我们将刚刚生成的testDLL.dll文件,和一个testDLL.lib放到我们Main项目的debug目录下面,
在这里插入图片描述
2我们来讲解一下DLL的隐式加载和显示加载
我们在Mainmain.cpp中做修改,这里先来演示一下隐式加载

#include <iostream>
#include <WIndows.h>

extern _declspec(dllimport)int add(int a, int b);
int main() {
	//这里结果是7
	std::cout << add(3, 4);
} 

显示加载可能就不需要我们去添加依赖项了,我们可以不用直接加载进来

#include <iostream>
#include <WIndows.h>

int main() {
	//HINSTANCE 实例句柄
	HINSTANCE hInst = LoadLibraryA("testDLL.dll");
	if (!hInst) {
		std::cout << "load testDll.dll error";
		return -1;
	}
	typedef int (*hInstPtr)(int a, int b);
	hInstPtr tmp= (hInstPtr)GetProcAddress(hInst, "add");
	std::cout << tmp(4,5);
}

接下来执行代码,你会惊奇的发现,此时我们的代码已经报错,what?什么鬼?
在这里插入图片描述
让我们通过开发者命令行 dumpbin export testDLL.dll来查看我们的dll文件里面的函数:

在这里插入图片描述
你会发现,我们的add函数后面是一串的字符,问题就在这里了,
在这里插入图片描述
动态链接库是通过编译后在进行链接,然后再执行的,应为C++程序是支持重载,会有相同的函数名字,所以我们编译器会产生名字改编,我们是用add@@YAHHH@Z去调用这个函数调用一下,就能顺利的执行dll里面的AIP函数
在这里插入图片描述那我们要怎么解决名字改编这个问题呢?我们将我们将DLL1.h和DLL1.cpp
文件加一个关键字

/*在Dll.h 这里进行了C系列代码编译,由于C++支持函数重载,而我们的C语言不支持函数重载,所以目前这个demo程序是可以运行的,但是如果是类局不能导出了*/
extern "C" _declspec(dllexport)int _cdecl add(int a, int b);
//DLL1.cpp

#include "pch.h"
#include "DLL_1.h"

extern "C" _declspec(dllexport)int _cdecl add(int a, int b)
{
	return a + b;
}

接下来可能会有小伙伴对我们的_cdecl有点蒙蔽了,这边先来介绍一下,__stdcall和__cdecl都是函数调用约定关键字
1.__stdcall:参数由右向左压入堆栈;堆栈由函数本身清理。
2.__cdecl:参数也是由右向左压入堆栈;但堆栈由调用者清理。

_declspec(dllexport)int _stdcall add(int a, int b);
//DLL1.cpp

#include "pch.h"
#include "DLL_1.h"

_declspec(dllexport)int _stdcall add(int a, int b)
{
	return a + b;
}

我们默认的函数调用约定是_cdecl,当我们使用_stdcall 的时候,编译器会让函数前面加一个下划线,后面再跟上函数的字节,发生了名字改编,我们在调用函数,就会出现
在这里插入图片描述
(这里在调试的时候忘记加_stdcall 了,但是加了还是会发生名字改编)当我们再次使用dumpbin 查看,还是会出现之前的名字改编情况,而我们使用C语言编译方法,却不能导出C++的类,添加一个.def文件
在这里插入图片描述

LIBRARY		//即使我们调用_stdcall约定,也不会发生名字改编
	add		//字符串
EXPORTS

此时我们使用_stdcall就不会发生名字改编了,我们在Main.cpp测试一下我们的接口

#include <iostream>
#include <WIndows.h>

int main() {
	//HINSTANCE 实例句柄
	HINSTANCE hInst = LoadLibraryA("testDLL.dll");
	if (!hInst) {
		std::cout << "load testDll.dll error";
		return -1;
	}
	typedef int (*hInstPtr)(int a, int b);
	//我们要保持和接口申明类型一致,Dll1.h和Dll1.cpp文件,也需要改,我们要保持调用约定一致
	hInstPtr tmp= (_stdcall hInstPtr)GetProcAddress(hInst, "add");
	std::cout << tmp(4,5);
}

二。静态库
先说说静态库和动态库的区别,可能就会稍微简单一些,静态库的话,不支持类似于dll的显示GetProcAddress函数,可以不需要头文件,申明就可以直接调用里面的AIP,而静态库(lib)必须要有库函数的申明文件,“ 动态库是在编译后加载到程序里面,而静态库就是在编译时加载到程序里面” ,方便移值,

2这边来介绍一下静态库的使用吧,这边创建了一个lib项目在里面添加了mylib.h文件和.cpp文件

#include "mylib.h"

int add(int a, int b){

	return a + b;
}

//main.cpp 我们在这测试lib库
#include "mylib.h"
#include <iostream>
#pragma comment(lib,"mylib.lib);	
//我们生成的lib库的名字,第二种方式就是之前用到过的从项目属性里面设置我们的lib路径
int main(){
	std::cout<<add(3+34);

}

总结:
1静态库是在编译时链接到目标代码中,所以他的移值性好,但是体积比较大,而我们动态库编译时不会链接到代码中,而是在程序运行的时候,加载进去,,他所占的体积会比较小,使用动态库好处是我们不需要去在去再去包含库的.h文件,可以更好的封装给其他小伙伴使用,关于动态库和静态库其他的问题可以自行查找一下,就讲到小编我累了

作者:Miles

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值