python调用C/C++动态库

本文详细介绍了在Python中如何调用C/C++编译的动态链接库(DLL),包括动态库的作用、加载方式以及如何通过ctypes库指定函数命名和参数类型。通过实例展示了值类型、指针类型、引用类型和结构体类型的参数处理,总结了调用DLL的步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文以windows环境下的.dll动态链接库为背景展开,有关linux下的.so动态链接库的相关用法会在另外一篇文章中展开讲解。

1. 背景知识

一直以来python都被称为胶水语言,能够轻易地操作其他程序,轻易地包装使用其他语言编写的库。下面简单介绍下如何使用python来调用C/C++编写的动态库。首先了解下动态链接库及C/C++动态库的区别。

1.1 动态链接库

使用VS2017创建动态链接库DllDemo,代码如下:

//整型相加
 __declspec(dllexport) int addInt(int a, int b) 
{
	return a + b;
}
//浮点型相加
__declspec(dllexport) float addFloat(float a, float b) 
{
	return a + b;
}
//传递float指针类型参数
__declspec(dllexport) void changeFloat(float *a) 
{
	*a = 100.00;
}
//传递char指针类型参数,返回指针类型参数
__declspec(dllexport) char* pointCh(char *a) 
{
	return a;
}

使用dumpbin.exe工具,在cmd命令行使用

dumpbin.exe -exports DllDemo.dll

查看动态库的导出函数如下:
C++动态库的导出函数列表

从上图可以看出动态库导出了4个函数,与上面代码中的导出函数一致。但是函数名长得很奇怪,这是因为在编译链接时,C++会按照自己的规则篡改函数的名称,这一过程称为"名字改编",C++支持函数重载,就是在函数名字改编阶段记录下函数的相关参数信息。

C++标准并没有定义名字改编的标准,因此会导致不同编译器编译出来的动态库不能通用。

1.2 extern "C"作用

相比之下,C标准规定了名字改编的标准,extern "C"指示编译器在编译代码是按照C的标准进行编译。
在上述代码的每个函数名前都加上extern "C"关键字。

extern "C" __declspec(dllexport) int addInt(int a, int b) 
{
	return a + b;
}

extern "C" __declspec(dllexport) float addFloat(float a, float b) 
{
	return a + b;
}

extern "C" __declspec(dllexport) void changeFloat(float *a) 
{
	*a = 100.00;
}

extern "C" __declspec(dllexport) char* pointCh(char *a) 
{
	return a;
}

查看动态库的导出函数,可以看到导出的函数名和函数的定义是一致的。
C动态库的导出函数

如果extern "C"修饰的函数进行了重载,则会在编译时报错,因为C语言并不支持函数的重载。

1.3 动态链接库加载方式

1.3.1 隐式链接

隐式链接需要.dll .lib文件

#include <iostream>
#include <Windows.h>
using namespace std;

//隐式加载需要导入动态库的导入库
#pragma comment(lib, "../Debug/DllDemo.lib")

//dll中导出的函数通过直接声明或者包含头文件的方式
extern "C" _declspec(dllimport) int addInt(int a, int b);
extern "C" _declspec(dllimport) float addFloat(float a, float b);

int main()
{
    //调用动态库中的函数
	cout << addInt(1, 2) << endl;
	cout << addFloat(1.25, 2.25) << endl;
}

通过vs自带的工具lib,通过命令lib /list XXXX.lib,可以查看一个.lib文件是静态库还是导入库

1.3.2 显式链接

显式链接需要.dll, 需要事先知道导出函数的签名,不需要.lib和.h文件

#include <iostream>
#include <Windows.h>
using namespace std;

//声明导出函数的类型
typedef int(*addInt)(int a, int b);
typedef float(*addFloat)(float a, float b);

int main()
{
	HINSTANCE hDLL;

	//定义导出函数
	addInt addInt_;
	addFloat addFloat_;
	
	//加载动态库,开辟内存
	hDLL = LoadLibrary("./DllDemo.dll");
	if (hDLL != NULL)
	{
		//获取导出函数
		addInt_ = (addInt)GetProcAddress(hDLL, "addInt");
		if (addInt_)
		{
			cout << addInt_(1, 2) << endl;
		}

		addFloat_ = (addFloat)GetProcAddress(hDLL, "addFloat");
		if (addFloat_)
		{
			cout << addFloat_(1.25, 2.25) << endl;
		}
		//卸载动态库,释放内存
		FreeLibrary(hDLL);
	}

	return 0;
}

使用显式加载的好处:

  • 通过显式加载动态库的方式可以根据需要加载相应的函数,随时可以卸载,通过判断句柄的方式,不会因为找不到dll导致程序无法启动。
  • 如果程序需要访问十多个dll,采用动态加载的方式可以减少程序的启动时间,减小程序占用的内存

2. python操作动态库

python操作动态库是通过ctypes这个内建的包,官方文档ctypes。因为上述C++动态库“名字改编”的问题,导致直接使用C++代码中函数名字时无法调用,必须使用经过名字改编之后的函数名,在使用C++编译的动态链接库时,最好使用extern "C"来辅助,可以通过对C++动态库进行简单的封装转换为C动态库,这样可以在使用时直接调用动态库中定义的函数即可,不需要考虑函数“名字改编”的问题。

先上代码:

#--*--utf8--*--
from c
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值