C调用MATLAB编译的复数矩阵DLL报错排查及复数读取方法
问题排查与修复方案
1. 稀疏矩阵索引报错核心原因
报错Error using sparse: Index into matrix must be positive本质是创建稀疏矩阵时传入的行/列索引存在0或负数,你的代码存在4个直接触发该问题的硬伤:
- 矩阵创建维度和实际数据不匹配:MATPOWER case9的bus矩阵为9行13列、gen矩阵为3行21列,但你创建
Bus时指定为9行17列、Gen为3行25列,多分配的内存未初始化,为随机垃圾值;虽然你拷贝了对应长度的有效数据,但一旦MATLAB读取到未初始化的列,就会得到错误数值。 - 存储顺序不匹配:C语言原生二维数组是行优先存储(同一行元素连续存放),MATLAB的mxArray是列优先存储(同一列元素连续存放),直接用
memcpy把C二维数组拷贝到mxArray内存,会导致整个矩阵元素完全错位,读取gen(:,1)(机组所在母线号)、gen(:,8)(机组投运状态)时拿到的根本不是对应列的正确值,很容易出现0或负数的索引。 - baseMVA参数赋值错误:你创建的
var是double类型1x1矩阵,但memcpy时写入了int类型的100,int和double二进制存储格式完全不同,MATLAB读取该参数时得到的是一个接近0的乱码浮点数,即使稀疏矩阵创建成功,最终计算结果也会完全错误。 - 提前创建输出矩阵无意义:
mlfMakeSbus会自动为输出参数sbus分配内存,你提前创建的9x1复数矩阵会被覆盖,反而会造成内存泄漏。
2. 报错排查步骤
- 第一步对齐矩阵维度:创建mxArray时维度严格和实际读入的矩阵一致,
Bus创建为9行13列,Gen创建为3行21列,不要多开冗余空间。 - 第二步修正数据写入逻辑:不要直接用
memcpy拷贝C的二维数组,要么读csv时按列优先顺序写入mxArray内存,要么循环逐元素赋值,确保mxGetPr(Bus)[row + col*9]对应的是bus矩阵第row+1行、第col+1列的数值(MATLAB为1基索引)。 - 第三步修正baseMVA赋值:不要用int类型拷贝,直接赋值为double类型即可:
*mxGetPr(var) = 100.0; - 第四步入参校验:调用
mlfMakeSbus前,打印关键位置的数值验证正确性:比如gen矩阵第8列(投运状态,对应C端列索引7,列优先下偏移量为7*3)的3个值都应为1,gen矩阵第1列(母线号,对应C端列索引0)的3个值应为1、2、3,如果打印结果不对,说明数据写入顺序或读csv的逻辑有问题。 - 第五步移除提前创建
sbus的代码,将sbus初始化为NULL即可,交给编译后的函数自行分配输出内存。
3. C端读取MATLAB复数矩阵的正确方法
MATLAB的复数类型mxArray分两块存储:实部数据存在mxGetPr()返回的指针指向的内存,虚部数据存在mxGetPi()返回的指针指向的内存,你之前只读取了实部指针,完全丢失了虚部数据。
正确读取逻辑示例:
double *real_ptr = mxGetPr(sbus); double *imag_ptr = mxGetPi(sbus); size_t elem_num = mxGetM(sbus) * mxGetN(sbus); // 9行1列的列优先矩阵直接按顺序遍历即可 for (int idx = 0; idx < elem_num; idx++) { printf("%.4f %+.4fi\n", real_ptr[idx], imag_ptr[idx]); }
读取前建议增加格式校验:判断返回值是否为复数类型、维度是否符合预期,避免访问非法内存。
4. 修正后的完整调用逻辑参考
mxArray *Bus = NULL, *Gen = NULL, *var = NULL, *sbus = NULL; double B[9][13], G[3][21]; // 从csv读取B、G矩阵的逻辑,确保数值和MATPOWER case9完全一致,列顺序对齐 // 创建维度匹配的mxArray Bus = mxCreateDoubleMatrix(9, 13, mxREAL); Gen = mxCreateDoubleMatrix(3, 21, mxREAL); var = mxCreateDoubleMatrix(1, 1, mxREAL); // 逐元素按列优先顺序写入,避免行优先/列优先错位 double *bus_pr = mxGetPr(Bus); for (int col = 0; col < 13; col++) { for (int row = 0; row < 9; row++) { bus_pr[row + col*9] = B[row][col]; } } double *gen_pr = mxGetPr(Gen); for (int col = 0; col < 21; col++) { for (int row = 0; row < 3; row++) { gen_pr[row + col*3] = G[row][col]; } } // 正确赋值baseMVA *mxGetPr(var) = 100.0; if (!makeSbusInitialize()) { fprintf(stderr, "库初始化失败\n"); goto EXIT; // 统一走资源释放逻辑 } // 调用函数 mlfMakeSbus(1, &sbus, var, Bus, Gen); // 读取并打印结果 if (sbus != NULL && mxIsComplex(sbus) && mxGetM(sbus) ==9 && mxGetN(sbus) ==1) { double *real_ptr = mxGetPr(sbus); double *imag_ptr = mxGetPi(sbus); for (int idx = 0; idx <9; idx++) { printf("Sbus[%d] = %.4f %+.4fi\n", idx+1, real_ptr[idx], imag_ptr[idx]); } } makeSbusTerminate(); EXIT: // 统一释放所有mxArray内存,避免泄漏 if (Bus) mxDestroyArray(Bus); if (Gen) mxDestroyArray(Gen); if (var) mxDestroyArray(var); if (sbus) mxDestroyArray(sbus);
注意:所有mxArray创建后必须显式调用
mxDestroyArray释放,否则会出现内存泄漏;不要手动释放mxGetPr/mxGetPi返回的指针,这部分内存由mxArray自行管理。
内容的提问来源于stack exchange,提问作者S2022
相关产品推荐
相关产品推荐

