C语言原地转置非方阵遇Valgrind未初始化值错误排查
非方阵原地转置的Valgrind未初始化值问题
场景说明
尝试在C语言中实现非方阵的原地转置,涉及变量定义:
****mats:指向动态分配三维数组的指针**ll:存储各矩阵行数的动态分配数组指针**lc:存储各矩阵列数的动态分配数组指针
实现代码(transpose_mat函数)
253 void transpose_mat(int ****mats, int **ll, int **lc, int k) { 254 int max_dim = (*lc)[k]; 255 if ((*ll)[k] >= (*lc)[k]) 256 max_dim = (*ll)[k]; 257 258 (*mats)[k] = realloc((*mats)[k], max_dim * sizeof(***mats)); 259 for (int i = 0; i < max_dim; ++i) 260 (*mats)[k][i] = realloc((*mats)[k][i], max_dim * sizeof(****mats)); 261 262 for (int i = 0; i < max_dim; ++i) { 263 for (int j = i + 1; j < max_dim; ++j) { 264 int tmp = (*mats)[k][i][j]; 265 (*mats)[k][i][j] = (*mats)[k][j][i]; 266 (*mats)[k][j][i] = tmp; 267 } 268 } 269 270 int tmp = (*lc)[k]; 271 (*lc)[k] = (*ll)[k]; 272 (*ll)[k] = tmp; 273 274 (*mats)[k] = realloc((*mats)[k], (*ll)[k] * sizeof(***mats)); 275 for (int i = 0; i < (*ll)[k]; ++i) { 276 (*mats)[k][i] = realloc((*mats)[k][i], (*lc)[k] * sizeof(****mats)); 277 } 278}
Valgrind输出信息
1 2 1 2 ----transpose_mat ==18654== Conditional jump or move depends on uninitialised value(s) ==18654== at 0x484DC80: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==18654== by 0x10A67A: transpose_mat (my_octave.c:260) ==18654== by 0x10B1CC: main (my_octave.c:436) ==18654== Uninitialised value was created by a heap allocation ==18654== at 0x484DCD3: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==18654== by 0x10A60C: transpose_mat (my_octave.c:258) ==18654== by 0x10B1CC: main (my_octave.c:436) ==18654== 2 1 1 2 ---- free_k_matrix ==18654== ==18654== HEAP SUMMARY: ==18654== in use at exit: 0 bytes in 0 blocks ==18654== total heap usage: 14 allocs, 14 frees, 5,212 bytes allocated ==18654== ==18654== All heap blocks were freed -- no leaks are possible ==18654== ==18654== For lists of detected and suppressed errors, rerun with: -s ==18654== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
问题
我忽略了什么问题?尝试在realloc mats的内存后初始化值,但最终出现了更多错误和段错误。
解答
核心问题分析
Valgrind的报错指向第260行的realloc,根源是第258行realloc扩展(*mats)[k]后,新增的行指针是未初始化的垃圾值。
原逻辑中,当原矩阵行数(*ll)[k]小于max_dim时,realloc会分配更多的行指针空间,但这些新的行指针并没有被初始化为有效地址,直接在第260行对它们调用realloc就会操作未初始化的指针,触发Valgrind的未初始化值警告。
另外,转置循环遍历到max_dim,但原矩阵只有(*ll)[k]行和(*lc)[k]列,超出原矩阵范围的元素属于未初始化内存,直接交换会引发额外的未定义行为。
修复步骤
- 初始化新增的行指针:在第258行
realloc之后,遍历新增的行索引(从原行数到max_dim-1),将这些行指针初始化为NULL,确保后续realloc能正确处理(realloc(NULL, size)等价于malloc(size))。 - 初始化新增内存区域:扩展行的列数后,对新增的元素进行初始化(比如设为0),避免转置时访问未初始化值。
- 添加内存分配失败检查:原代码未检查
realloc返回值,这是潜在的崩溃风险,必须补充错误处理逻辑。
修复后的代码示例
void transpose_mat(int ****mats, int **ll, int **lc, int k) { int old_rows = (*ll)[k]; int old_cols = (*lc)[k]; int max_dim = (old_rows >= old_cols) ? old_rows : old_cols; // 扩展行指针数组,初始化新增的行指针为NULL int** temp_mats_k = realloc((*mats)[k], max_dim * sizeof(int*)); if (temp_mats_k == NULL) { // 按需添加内存分配失败的处理逻辑 return; } (*mats)[k] = temp_mats_k; for (int i = old_rows; i < max_dim; ++i) { (*mats)[k][i] = NULL; } // 扩展每一行的列数,并初始化新增元素 for (int i = 0; i < max_dim; ++i) { int* temp_row = realloc((*mats)[k][i], max_dim * sizeof(int)); if (temp_row == NULL) { // 按需添加错误处理逻辑 return; } (*mats)[k][i] = temp_row; // 初始化新增的列元素 int start_col = (i < old_rows) ? old_cols : 0; for (int j = start_col; j < max_dim; ++j) { (*mats)[k][i][j] = 0; } } // 执行转置操作 for (int i = 0; i < max_dim; ++i) { for (int j = i + 1; j < max_dim; ++j) { int tmp = (*mats)[k][i][j]; (*mats)[k][i][j] = (*mats)[k][j][i]; (*mats)[k][j][i] = tmp; } } // 交换行列数 (*ll)[k] = old_cols; (*lc)[k] = old_rows; // 缩放到转置后的维度 temp_mats_k = realloc((*mats)[k], (*ll)[k] * sizeof(int*)); if (temp_mats_k == NULL) { // 按需添加错误处理逻辑 return; } (*mats)[k] = temp_mats_k; for (int i = 0; i < (*ll)[k]; ++i) { int* temp_row = realloc((*mats)[k][i], (*lc)[k] * sizeof(int)); if (temp_row == NULL) { // 按需添加错误处理逻辑 return; } (*mats)[k][i] = temp_row; } }
额外建议
非方阵的原地转置逻辑复杂且效率较低,更简单可靠的方式是直接分配转置后的新矩阵,复制元素后释放原矩阵,避免反复调整内存大小带来的问题。
内容的提问来源于stack exchange,提问作者cybiroot
相关产品推荐
相关产品推荐

