C++封装Python模块读取TSV后调用PyImport_Import出现段错误
C++封装Python模块导入TSV数据后出现段错误的问题记录
我使用C++作为Python模块的封装层,先读取TSV文件转换为numpy数组,再导入Python模块将numpy数组传入做后续分析。最初使用随机生成的数组测试时程序运行正常,但将数组替换为TSV导入的内容后,调用PyImport_Import导入模块时出现段错误。
以下是相关代码:
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #define PY_SSIZE_T_CLEAN #include <python3.8/Python.h> #include "./venv/lib/python3.8/site-packages/numpy/core/include/numpy/arrayobject.h" #include <stdio.h> #include <iostream> #include <stdlib.h> #include <random> #include <fstream> #include <sstream> int main(int argc, char* argv[]) { setenv("PYTHONPATH", ".", 0); Py_Initialize(); import_array(); static const int numberRows = 1000; static const int numberColumns = 500; npy_intp dims[2]{ numberRows, numberColumns }; static const int numberDims = 2; double(*c_arr)[numberColumns]{ new double[numberRows][numberColumns] }; // *********************************************************** // 这部分代码生成随机数组,和其余代码搭配运行正常 // // initialize random number generation // typedef std::mt19937 MyRNG; // std::random_device r; // MyRNG rng{r()}; // std::lognormal_distribution<double> lognormalDistribution(1.6, 0.25); // //populate array // for (int i=0; i < numberRows; i++) { // for (int j=0; j < numberColumns; j++) { // c_arr[i][j] = lognormalDistribution(rng); // } // } // *********************************************************** // *********************************************************** // 这部分代码从TSV读取数组,导致代码在PyImport_Import处失败 std::ifstream data("data.mat"); std::string line; int row = 0; int column = 0; while (std::getline(data, line)) { std::stringstream lineStream(line); std::string cell; while (std::getline(lineStream, cell, ' ')) { c_arr[row][column] = std::stod(cell); column++; } row++; column = 0; if (row > numberRows) { break; } } // *********************************************************** PyArrayObject *npArray = reinterpret_cast<PyArrayObject*>( PyArray_SimpleNewFromData(numberDims, dims, NPY_DOUBLE, reinterpret_cast<void*>(c_arr)) ); const char *moduleName = "cpp_test"; PyObject *pname = PyUnicode_FromString(moduleName); // *********************************************************** // 代码在此处失败 - 段错误 PyObject *pyModule = PyImport_Import(pname); // ....... // 后续还有其他代码未附上 }
问题原因与解决
最初无法复现问题逻辑:随机数组运行正常,TSV读取后就崩溃,而且崩溃点在导入模块的位置,和数组读取逻辑看似无关。
后续排查发现这是一个非常低级的错误:读取TSV的循环停止条件写为row > numberRows,当TSV文件行数超过预设的numberRows时,row会先自增到numberRows + 1才会触发break,导致c_arr[row]访问时越界写入,破坏了堆内存结构。内存被破坏后后续执行Python API调用时就触发了段错误。
将停止条件修改为row == numberRows后,程序运行完全正常。
这个问题可以作为常见低级错误的参考:内存越界的崩溃点不一定和越界操作的位置一致,写边界判断时要格外注意阈值条件的正确性。
内容的提问来源于stack exchange,提问作者CopyOfA
相关产品推荐
相关产品推荐

