静态库(lib)和动态库(dll)的使用


前言

  静态库动态库在windows软件开发当中很常用,好的软件架构很多功能都是以库的形式存在,假如该功能需要升级,则直接替换该库文件就行,不需要把整个程序进行升级。这样维护起来就比较方便。

  常见的库文件有.lib.dll两种形式,分别是静态库和动态库。

  静态库在形成exe文件时,全部被打包进去。

  动态库只是在运行的时候用到的函数才会被导入到exe中,可以动态的加载和卸载。


一、函数重载的理解

  C++中支持函数重载,C中不支持函数重载。

  C中同名的函数编译之后,函数名都相同,以VS举例,都是以下划线开头 “_”,所以如果调用,都不知道调用哪个函数,所以C不支持重载。
  cdecl就是这种调用方式,直接寻找原函数名,函数名不添加任何修饰。

在这里插入图片描述

  C++中同名的函数编译之后,编译器会在函数名中加入参数类型和参数个数等含义进行区分,即每个函数名都不同,可以进行区分,所以C++支持重载。(C++中的命名规则:Name-Mangling)
  (下面图片中显示的每个不同参数的同名函数,后面编译的函数名称都不同)
  stdcall这种调用方式,就会在原函数名上添加参数等修饰,用来区分函数重载时相同函数函数而函数体和参数不同的情况。

在这里插入图片描述


二、extern “C” 的理解

  上面提到C++在编译函数之后,会对其名字进行再次修改,增加参数信息,那么C++的函数和C的函数是无法互相调用的,因为编译后各自的函数名不同, 链接的时候会因为找不到函数名称而报错。但是实际当中却可以,说明进行过一定的处理。

  这就是 extern “C”的作用。

  其作用就是:将函数名按照C中生成函数名的方式去生成。

在这里插入图片描述

  这样的话,即使各自编译器生成函数名的方式不用,但都是按照C的标准来生成,那么可以互相调用,所以动态库如果要被外部调用,一般都要加这个修饰


三、静态库的调用

1.静态库的生成

(1)生成静态库(lib)

  首先点击静态库(Visual C++),然后选好目录之后,点击确定。

在这里插入图片描述
  可以去掉其他文件,只留下pch.h和pch.cpp即可,

在这里插入图片描述

在这里插入图片描述
静态库.h程序

#ifndef PCH_H
#define PCH_H

#include <iostream>


void Print();
int Add(int a, int b);

#endif //PCH_H

在这里插入图片描述
静态库.cpp程序

#include "pch.h"

using namespace std;
void Print()
{
    
    
	cout << "I am a smart boy!" << endl;
	cout << "666!" << endl;
	cout << "you are a pretty girl!" << endl;
}

int Add(int a, int b)
{
    
    
	return ((a)+(b));
}

  最后点击生成,就会看到有lib文件生成。

在这里插入图片描述


2.静态库的调用

  三种方式,程序导入,资源文件导入,编译器导入。

  无论哪种导入方式,均需要导入库的头文件,本文中需要添加的是pch.h文件,其.h的路径也需要加入,在工程-》属性-》C/C++目录-》附加目录里面添加.h文件的目录。

在这里插入图片描述
在这里插入图片描述

  1. 通过程序调入 (路径需要使用\ \

在这里插入图片描述

#include <iostream>
#include "pch.h"

#pragma comment(lib, "D:\\guoqing.zhang\\StaticLibrary\\StaticLib\\Debug\\StaticLib.lib")

int main()
{
    
    
	Print();
	std::cout << Add(3, 6) << std::endl;
}
  1. 资源文件导入
    在这里插入图片描述
  2. 编译器导入

  工程-》属性-》链接器-》常规 里面的附加库目录,要添加静态库路径。

在这里插入图片描述

  工程-》属性-》链接器-》输入 里面的附加依赖项,需要添加静态库的名称。

在这里插入图片描述

  无论哪种添加完成后,在主函数文件中加入“pch.h”,点击调试,则会看到运行结果。

在这里插入图片描述


四、动态库的调用

1.动态库的生成

  点击项目-》新建项目-》动态链接库DLL,选择好路径之后就可以生成工程。可以只留下一个.h和一个.cpp文件,其他均可删除。

在这里插入图片描述

在这里插入图片描述
动态库.h程序

#pragma once

#ifndef DLL_H
#define DLL_H

#define  LIB_API extern "C" __declspec(dllexport)
#include <iostream>

LIB_API void Demo();
LIB_API int Sub(int a, int b);
#endif //PCH_H

在这里插入图片描述
动态库.cpp程序

#include "Dll.h"
#include "pch.h"

LIB_API void Demo()
{
    
    
	Print();
	std::cout << Add(3, 5) << std::endl;
}

LIB_API int Sub(int a, int b)
{
    
    
	return ((a)-(b));
}

  没错,你看完之后,发现动态库可以调用静态库,本文当中的Dll库调用之前生成的StaticLib.lib,添加了静态库的头文件。

  这也是静态库和动态库的区别,动态库可以包含其他静态库或者动态库,而静态库则无法包含其他的静态库或者动态库。

  完成编程之后,点击生成,可以看到有.dll库生成,进入其文件夹,会发现有对应的.lib文件生成,这里面不是库程序,只是dll文件的函数名称和位置,方便找到.dll函数实体。

在这里插入图片描述
在这里插入图片描述
  如果发现有下面图中的错误这样的错误,那么是预编译头的问题,选择不使用预编译头即可正常完成编译。(预编译文件被删除。)

在这里插入图片描述
在这里插入图片描述

2.动态库的调用

隐式调用:将Dll.dll和Dll.lib和Dll.h放在工程目录下面。

在这里插入图片描述
  链接器-》输入添加附加依赖项,选择Dll.lib,

在这里插入图片描述
  Dll.lib添加在资源文件或者附件依赖项中都可以,然后主函数中添加“Dll.h”头文件,调用库中的函数,
在这里插入图片描述
  点击调试,可以正常运行。
在这里插入图片描述
显式调用:不添加.h文件和.lib文件,直接导入dll库,通过函数指针去调用库中的函数。具体流程如下:

1、导入dll库 LoadLibrary(库名称)
2、构造函数指针,要和库文件中函数一致,包括函数参数和类型。
3、通过函数指针获取函数地址,GetProcAddress(句柄,函数名/函数序号);(函数序号:函数在.h中声明的顺序)
4、利用函数指针调用函数

#include <iostream>
#include <Windows.h>
int main()
{
    
    
    std::cout << "Hello World!\n";
	//1、导入dll库
	HMODULE h = LoadLibrary(L"Dll.dll");
	//2、构造函数指针
	typedef int(*PSUM) (int, int);//
	//3、利用函数指针调用函数
	PSUM psum =(PSUM)GetProcAddress(h,"Sub");//利用函数名
	//PSUM psum = (PSUM)GetProcAddress(h, (char*)2);//利用函数序号
	std::cout << psum(11, 22) << std::endl << std::endl;

	typedef void(*PDemp) ();//
	//3、利用函数指针调用函数
	/*PSUM psum =(PSUM)GetProcAddress(h,"sum");*///利用函数名
	PDemp pdemo = (PDemp)GetProcAddress(h, (char*)1);//利用函数序号
	pdemo();

	//4、释放DLL
	FreeLibrary(h);
	return 0;
}

成功调用。
在这里插入图片描述

  如果发现函数名称无法找到,无法完成动态链接,那么可能是形成的库中函数名称和链接的函数名称不匹配。(存在一个是添加信息修改过的,一个是未修改过。)(下面图片中的错误)

  通过GetProcAddress函数获取dll中的函数,dll中导出的函数必须有extern "C"修饰,否则无法找到函数地址。(原因就是C++的Name-Mangling修改函数名称规则)。
在这里插入图片描述
在这里插入图片描述
实例代码下载

猜你喜欢

转载自blog.csdn.net/qq_34430371/article/details/111354711