使用Python ctypes将long类型vector按引用传递给C++函数的问题
解决Python ctypes传递C++ vector&的问题
你遇到的问题本质是ctypes无法直接识别C++标准库的vector类型——它是一个带有内部状态(指针、大小、容量)的复杂结构,直接用c_void_p传递相当于把一个无效的空指针塞给C++函数,必然会导致未定义行为(比如崩溃、垃圾值)。下面给你两种可行的解决方案,优先推荐第一种,兼容性和可靠性都更好。
方案一:改用C风格动态数组(推荐)
既然vector的大小不固定,我们可以在C++侧把计算结果转换成动态分配的long数组,同时返回数组的大小,再给Python提供一个释放内存的接口,避免泄漏。
修改后的C++代码(test.cpp)
#include <vector> #include <algorithm> // 用于std::copy extern "C" { // 函数参数:输入字符串a、b,输出数组的指针(指针的指针),输出数组大小的指针 int pke_decrypt(char *a, char *b, long** out_array, int* out_size) { // 这里是你的原有逻辑,生成vector<long> v std::vector<long> v; // 示例:填充一些测试数据 v.push_back(123); v.push_back(456); v.push_back(789); // 分配内存并复制数据到数组 *out_size = static_cast<int>(v.size()); *out_array = new long[*out_size]; std::copy(v.begin(), v.end(), *out_array); return 0; // 返回状态码,0表示成功 } // 必须提供释放内存的函数,否则会造成内存泄漏 void pke_free_long_array(long* array) { delete[] array; } }
对应的Python代码(test.py)
import ctypes # 加载编译好的动态库 _p = ctypes.CDLL('/home/electrical/Downloads/src/libpke.so') # 声明函数的参数类型和返回值类型 _p.pke_decrypt.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.POINTER(ctypes.c_long)), ctypes.POINTER(ctypes.c_int) ] _p.pke_decrypt.restype = ctypes.c_int _p.pke_free_long_array.argtypes = [ctypes.POINTER(ctypes.c_long)] _p.pke_free_long_array.restype = None def decrypt(input1, input2): # 初始化输出参数:数组指针和大小 out_array = ctypes.POINTER(ctypes.c_long)() out_size = ctypes.c_int() # 调用C++函数 result_code = _p.pke_decrypt( input1.encode('utf-8'), input2.encode('utf-8'), ctypes.byref(out_array), ctypes.byref(out_size) ) # 把C数组转换成Python列表 result_list = [] if out_size.value > 0: result_list = [out_array[i] for i in range(out_size.value)] # 释放C侧分配的内存,务必记得这一步! _p.pke_free_long_array(out_array) return result_list
方案二:直接操作C++ vector(不推荐,兼容性差)
如果一定要坚持用vector<long>&,你需要模拟vector的内存布局(不同编译器/标准库版本可能不一样,比如GCC和MSVC的vector结构就有差异),同时需要C++侧提供创建、销毁vector的接口。
修改后的C++代码
#include <vector> extern "C" { // 原有解密函数,接收vector引用 int pke_decrypt(char *a, char *b, std::vector<long>& v) { // 原有逻辑填充v v.push_back(10); v.push_back(20); v.push_back(30); return 0; } // 创建空的vector<long>,返回指针 std::vector<long>* create_vector_long() { return new std::vector<long>(); } // 销毁vector,避免内存泄漏 void destroy_vector_long(std::vector<long>* v) { delete v; } }
对应的Python代码
import ctypes # 模拟GCC下std::vector<long>的内存结构(三个指针:起始、当前末尾、容量末尾) class VectorLong(ctypes.Structure): _fields_ = [ ("_M_start", ctypes.POINTER(ctypes.c_long)), ("_M_finish", ctypes.POINTER(ctypes.c_long)), ("_M_end_of_storage", ctypes.POINTER(ctypes.c_long)) ] _p = ctypes.CDLL('/home/electrical/Downloads/src/libpke.so') # 声明各个函数的类型 _p.create_vector_long.argtypes = [] _p.create_vector_long.restype = ctypes.POINTER(VectorLong) _p.destroy_vector_long.argtypes = [ctypes.POINTER(VectorLong)] _p.destroy_vector_long.restype = None _p.pke_decrypt.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(VectorLong) ] _p.pke_decrypt.restype = ctypes.c_int def decrypt(input1, input2): # 创建C++侧的空vector v_ptr = _p.create_vector_long() # 调用解密函数,传递vector的指针(相当于引用) result_code = _p.pke_decrypt( input1.encode('utf-8'), input2.encode('utf-8'), v_ptr ) # 计算vector的大小:末尾指针减去起始指针 vec_size = ctypes.c_long(v_ptr.contents._M_finish - v_ptr.contents._M_start).value # 提取所有元素到Python列表 result_list = [v_ptr.contents._M_start[i] for i in range(vec_size)] # 销毁vector,释放内存 _p.destroy_vector_long(v_ptr) return result_list
为什么不推荐方案二?
std::vector的内部结构是未标准化的,不同编译器(GCC/Clang/MSVC)、不同标准库版本的布局可能完全不同。比如MSVC的vector就不是三个指针,而是包含更多成员。这意味着你的代码换个环境就可能崩溃,维护成本极高。
而方案一用纯C风格的接口,所有类型都是ctypes原生支持的,兼容性拉满,也更容易调试和维护。
内容的提问来源于stack exchange,提问作者4am
相关产品推荐
相关产品推荐

