C语言Matrix结构体递增赋值double值触发崩溃问题排查
崩溃原因定位
你的内存分配、TestMatrix遍历赋值逻辑本身没有问题,崩溃根源是DoGaussJordan函数存在堆内存越界写入问题,属于C语言典型的未定义行为:全1矩阵输入时越界写入的位置刚好没有破坏关键内存结构,所以程序看似正常;输入递增序列矩阵时越界刚好踩中堆内存的管理元数据,直接触发程序崩溃。
具体bug有两处:
- 第一处是矩阵A的主元归一化循环越界:C语言数组下标从0开始,长度为C的数组合法下标范围是0~C-1,但你写的循环初始j值为
A.C,直接访问了每行分配的内存块之外的地址:
// 错误代码:j从A.C开始,访问越界 for( int j = A.C; j >= i; j-- ) { A.Data[i][j] /= A.Data[i][i]; }
- 第二处是行交换操作时B矩阵的遍历长度错误:交换行时你用A的列数
A.C作为B矩阵的遍历上限,没有单独判断B的列数,当A和B列数不一致时必然越界。你当前测试用例里A和B都是3*3,这处bug暂时没触发,但属于潜在风险。
为什么两种赋值方式表现不同
全1矩阵做选主元判断时,所有行同位置元素的绝对值完全相等,不会触发行交换逻辑;同时越界写入的位置刚好是malloc分配内存时预留的对齐填充字节,没有破坏堆块的管理信息,所以不会立刻崩溃。
你用1~9递增填充的3阶矩阵本身是奇异矩阵(行列式为0),消元过程中会走到零主元判断分支,加上越界写刚好覆盖了堆块的头部管理数据,破坏了内存分配器的内部结构,后续操作直接触发内存访问异常崩溃。
修复方案
- 修正归一化步骤的循环边界,把j的初始值从
A.C改为A.C-1:
// 修复后 for( int j = A.C - 1; j >= i; j-- ) { A.Data[i][j] /= A.Data[i][i]; }
- 拆分A、B矩阵的行交换循环,分别按各自列数遍历,避免越界:
// 修复后:单独交换A的行 for( j=0; j<A.C; j++ ) { temp=A.Data[i][j]; A.Data[i][j]=A.Data[k][j]; A.Data[k][j]=temp; } // 单独交换B的行,按B的列数遍历 for( j=0; j<B.C; j++ ) { temp=B.Data[i][j]; B.Data[i][j]=B.Data[k][j]; B.Data[k][j]=temp; }
- 可选优化:当前所有矩阵操作函数都是值传递Matrix结构体,虽然Data指针能正确访问到矩阵数据,但每次调用都会拷贝整个结构体,大矩阵场景下有额外性能开销,建议改成传Matrix指针的形式,比如
void DoGaussJordan(Matrix *A, Matrix *B)。
内容的提问来源于stack exchange,提问作者tisjeboijeroen
相关产品推荐
相关产品推荐

