使用ctypes调用C++返回二维数组报访问违规错误求解
错误根因
空指针访问错误是C++和Python两端的类型、内存布局、返回值不匹配共同导致的,核心错误点如下:
C++端错误
- 函数嵌套定义:在
group_coordinates函数内部重复定义同名函数,属于语法错误,编译出来的函数逻辑不符合预期。 - 入参类型不匹配:Numpy的二维数组在内存中是连续存储的一维块,不是
int**二级指针,用二级指针取值相当于把第一个元素的值当地址访问,直接触发空指针。 - 数组长度获取错误:
sizeof(coordinates)拿到的是指针本身的大小(64位环境下为8字节),不是传入数组的元素个数。 - 返回值类型不兼容:ctypes不支持直接解析C++的
vector<tuple<int,int>>类型,直接返回的内存布局完全无法被Python识别。 - 缺少
mean_xy函数定义,代码本身无法编译通过。
Python端错误
- 函数名不匹配:加载dll之后调用的是
c_lib.method,但C++导出的函数名是group_coordinates。 - 入参维度缺失:仅传了数组和max_deviation,没有传递数组长度,C++端无法识别传入的坐标点总数。
- 返回值处理错误:直接返回C++栈上的内存会被自动释放,就算拿到指针也是野指针,需要手动在堆上分配返回数组,同时把数组长度通过出参返回给Python。
修正后代码
C++端代码
#include <vector> #include <tuple> #include <cmath> using namespace std; // 补全缺失的mean_xy函数,可根据需求调整逻辑 tuple<int, int> mean_xy(vector<int>& x_list, vector<int>& y_list) { long long sum_x = 0, sum_y = 0; int len = x_list.size(); for (int i = 0; i < len; i++) { sum_x += x_list[i]; sum_y += y_list[i]; } return make_tuple((int)(sum_x / len), (int)(sum_y / len)); } extern "C" { // 调整接口:入参为连续存储的int64一维数组(对应numpy二维数组)、坐标点个数、最大偏差 // 出参p_out_len用来返回结果数组的长度,返回值是堆上分配的int64数组,每个坐标占2个元素 __declspec(dllexport) long long* group_coordinates(long long* coordinates, int point_count, int max_deviation, int* p_out_len) { vector<vector<int>> mean_x_groups = {{(int)coordinates[0]}}; vector<vector<int>> mean_y_groups = {{(int)coordinates[1]}}; vector<tuple<int, int>> grouped_coordinates; int mean_x, mean_y; // 遍历所有坐标点,i从1开始,第一个点已经完成初始化 for(int i = 1; i < point_count; i++) { int x = (int)coordinates[i*2]; int y = (int)coordinates[i*2 + 1]; bool found = false; // 检查所有已有分组 for(int index = 0; index < mean_x_groups.size(); index++) { tie(mean_x, mean_y) = mean_xy(mean_x_groups[index], mean_y_groups[index]); if (abs(x - mean_x) <= max_deviation && abs(y - mean_y) <= max_deviation) { mean_x_groups[index].push_back(x); mean_y_groups[index].push_back(y); found = true; break; } } // 未找到匹配分组则新建分组 if (!found) { mean_x_groups.push_back({x}); mean_y_groups.push_back({y}); } } // 生成结果 for (int index = 0; index < mean_x_groups.size(); index++) { tie(mean_x, mean_y) = mean_xy(mean_x_groups[index], mean_y_groups[index]); grouped_coordinates.push_back(make_tuple(mean_x, mean_y)); } // 堆上分配返回数组,用完需要手动释放 *p_out_len = grouped_coordinates.size(); long long* res = new long long[*p_out_len * 2]; for (int i = 0; i < *p_out_len; i++) { res[i*2] = get<0>(grouped_coordinates[i]); res[i*2 + 1] = get<1>(grouped_coordinates[i]); } return res; } // 提供内存释放接口,避免内存泄漏 __declspec(dllexport) void free_mem(long long* p) { delete[] p; } }
Python端代码
import ctypes import numpy as np c_lib = ctypes.cdll.LoadLibrary(r"C:\Users\~user~\Desktop\~folder~\file.dll") # 配置函数类型 c_lib.group_coordinates.restype = ctypes.POINTER(ctypes.c_int64) c_lib.group_coordinates.argtypes = [ np.ctypeslib.ndpointer(dtype=ctypes.c_int64, ndim=2, flags='C_CONTIGUOUS'), ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int) ] c_lib.free_mem.argtypes = [ctypes.POINTER(ctypes.c_int64)] # 构造输入 x = [[0,1], [2,3], [1,1], [86,100], [87,99], [100,100]] array = np.array(x, dtype = ctypes.c_int64) point_count = array.shape[0] max_deviation = 5 out_len = ctypes.c_int() # 调用函数 res_ptr = c_lib.group_coordinates(array, point_count, max_deviation, ctypes.byref(out_len)) # 指针转numpy数组 res_array = np.ctypeslib.as_array(res_ptr, shape=(out_len.value, 2)).copy() print(res_array) # 释放C++端分配的内存 c_lib.free_mem(res_ptr)
注意事项
- 编译C++代码时需选择和Python环境一致的架构(同是64位或同是32位),否则会调用失败。
- 如不想手动处理内存和类型匹配,可改用pybind11做C++ Python绑定,开发成本更低。
内容的提问来源于stack exchange,提问作者Marshall Merz
相关产品推荐
相关产品推荐

