使用ctypes实现Python调用C++ DLL传参报错:内存访问违规
问题分析与解决方案
核心错误原因
- 参数类型不匹配:Python直接传递字符串给C的
char*参数时,ctypes默认会转为宽字符(wchar_t*),但C函数期望窄字符指针,导致内存读取异常。 - C++代码内存越界:第二个循环中
i的取值范围超过了r_data/s_data的数组长度(数组仅len个元素,但i最大到len*2),直接访问非法内存触发访问违规。 - 返回值类型不兼容:C函数返回
std::string(C专属类型),但ctypes指定返回值为c_char_p(C风格字符串指针),两者内存布局完全不同,导致解析错误。 - 缺少参数类型声明:未指定
lib.encode.argtypes,ctypes无法正确处理参数转换逻辑。
修正方案
方案一:C++返回C风格字符串(需手动管理内存)
修正后的C++ DLL代码
#include <cstdlib> #include <ctime> #include <cstring> // 用extern "C"避免C++名字修饰,确保Python能找到函数 extern "C" __declspec(dllexport) char* encode(const char* data, unsigned int len) { // 仅初始化一次随机数生成器 static bool rand_init = false; if (!rand_init) { srand(time(nullptr)); rand_init = true; } // 分配结果缓冲区(+1用于存储字符串结束符) char* res_data = new char[len * 2 + 1]; memset(res_data, 0, len * 2 + 1); for (unsigned int i = 0; i < len; ++i) { unsigned char chr_num = static_cast<unsigned char>(data[i]); unsigned short salt = static_cast<unsigned short>(rand() % 512 + 512); unsigned short encrypted = chr_num + salt; // 将加密值拆分为高低字节存入缓冲区 res_data[2*i] = static_cast<char>(encrypted & 0xFF); res_data[2*i + 1] = static_cast<char>((encrypted >> 8) & 0xFF); } return res_data; } // 提供内存释放函数,让Python回收C++分配的内存 extern "C" __declspec(dllexport) void free_char_ptr(char* ptr) { delete[] ptr; }
修正后的Python代码
import ctypes import os # 加载DLL lib_path = os.path.join(os.getcwd(), "testDllForPy.dll") lib = ctypes.CDLL(lib_path) # 声明函数参数与返回值类型 lib.encode.argtypes = [ctypes.c_char_p, ctypes.c_uint] lib.encode.restype = ctypes.c_char_p lib.free_char_ptr.argtypes = [ctypes.c_char_p] lib.free_char_ptr.restype = None def encode(data: str) -> str: # 将Python字符串转为UTF-8字节流,匹配C++的char*类型 data_bytes = data.encode('utf-8') # 调用C++函数 result_ptr = lib.encode(data_bytes, len(data_bytes)) # 读取C风格字符串并转为Python字符串 result_str = ctypes.string_at(result_ptr).decode('utf-8') # 释放C++分配的内存 lib.free_char_ptr(result_ptr) return result_str # 测试 if __name__ == "__main__": print(encode("Hello"))
方案二:Python提供缓冲区(自动管理内存,更安全)
这种方式避免手动内存管理,由Python负责缓冲区的创建与回收。
修正后的C++ DLL代码
#include <cstdlib> #include <ctime> #include <cstring> extern "C" __declspec(dllexport) void encode(const char* data, unsigned int len, char* output_buf, unsigned int buf_len) { static bool rand_init = false; if (!rand_init) { srand(time(nullptr)); rand_init = true; } // 检查缓冲区长度是否足够 if (buf_len < len * 2 + 1) { memset(output_buf, 0, buf_len); return; } memset(output_buf, 0, len * 2 + 1); for (unsigned int i = 0; i < len; ++i) { unsigned char chr_num = static_cast<unsigned char>(data[i]); unsigned short salt = static_cast<unsigned short>(rand() % 512 + 512); unsigned short encrypted = chr_num + salt; output_buf[2*i] = static_cast<char>(encrypted & 0xFF); output_buf[2*i + 1] = static_cast<char>((encrypted >> 8) & 0xFF); } }
修正后的Python代码
import ctypes import os lib_path = os.path.join(os.getcwd(), "testDllForPy.dll") lib = ctypes.CDLL(lib_path) # 声明函数参数类型 lib.encode.argtypes = [ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint] lib.encode.restype = None def encode(data: str) -> str: data_bytes = data.encode('utf-8') data_len = len(data_bytes) # 计算所需缓冲区长度 buf_len = data_len * 2 + 1 # 创建Python管理的缓冲区 output_buf = ctypes.create_string_buffer(buf_len) # 调用C++函数写入缓冲区 lib.encode(data_bytes, data_len, output_buf, buf_len) # 转换为Python字符串 return output_buf.value.decode('utf-8') # 测试 if __name__ == "__main__": print(encode("Hello"))
内容的提问来源于stack exchange,提问作者MrAbsurd
相关产品推荐
相关产品推荐

