迷宫求解器中重分配动态数组的赋值异常问题排查
问题描述
在使用realloc()重分配动态数组后,存储值出现异常。开发迷宫求解器时,用struct tile类型的一维数组tile_array作为迷宫的内部表示,每个元素对应输入文件中的一个字符,结构体包含字符、距离、行列等附加数据。迷宫由包含该数组的struct maze表示,因输入文件长度未知,需动态分配内存。循环中创建struct tile并赋值到数组末尾的步骤正常,但调用realloc()扩容后,后续存储元素时printf()输出的所有参数均异常,无法定位问题。
原代码
#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <assert.h> struct tile { // should be one element in tile array bool visited; char type; // '#' / ' ' / 'X' / 'o' size_t distance; // distance in tile_array size_t row; size_t col; struct tile* previous; // pointer to previous tile }; struct maze { struct tile* tile_array; // 1D array of <tile> structs size_t length; // length of tile_array size_t *rows_lengths; // [row1_len, row2_len, ...] size_t row_count; // count of rows used }; // 假设已实现的非法字符检查函数 bool illegal_input_char(char c) { return !(c == '#' || c == ' ' || c == 'X' || c == 'o' || c == '\n'); } bool maze_create(struct maze *maze, FILE *file) { size_t array_length = 4; // allocate tile_array struct tile *tile_array = malloc(array_length * sizeof(struct tile)); if (tile_array == NULL) { return false; } // allocate rows_lengths array size_t rows_lengths_count = 4; size_t *rows_lengths = malloc(rows_lengths_count * sizeof(size_t)); if (rows_lengths == NULL) { free(tile_array); tile_array = NULL; return false; } char curr_char; size_t curr_distance = 0; size_t curr_row = 0; size_t curr_col = 0; struct tile *previous = NULL; while ((curr_char = fgetc(file)) != EOF) { assert(curr_distance <= array_length); assert(curr_row <= rows_lengths_count); // allocate more memory for tile_array if (curr_distance == array_length) { array_length += 4; struct tile *tmp = realloc(tile_array, (array_length * sizeof(struct tile))); if (tmp == NULL) { free(tile_array); tile_array = NULL; free(rows_lengths); rows_lengths = NULL; return false; } tile_array = tmp; tmp = NULL; } // allocate more memory for rows_lengths if (rows_lengths_count == curr_row) { rows_lengths_count += 4; size_t *tmp = realloc(rows_lengths, (rows_lengths_count * sizeof(size_t))); if (tmp == NULL) { free(tile_array); tile_array = NULL; free(rows_lengths); rows_lengths = NULL; return false; } rows_lengths = tmp; tmp = NULL; } if (curr_char == '\n') { // set variables & continue } if (illegal_input_char(curr_char)) { // invalid input, free memory & return false } // create new tile and insert to the maze struct tile curr_tile = {false, curr_char, curr_distance, curr_row, curr_col, previous}; tile_array[curr_distance] = curr_tile; printf("WRITTEN CHAR: '%lu', direction:%2lu", ((*maze).tile_array[curr_distance]).type, ((*maze).tile_array[curr_distance]).distance); curr_col++; curr_distance++; previous = &curr_tile; } (*maze).length = curr_distance; (*maze).row_count = curr_row - 1; return true; }
问题分析与修复
核心问题1:未关联动态数组到maze结构体
函数内部分配了tile_array和rows_lengths,但始终未将这两个指针赋值给*maze的对应成员。printf中访问(*maze).tile_array[curr_distance]时,(*maze).tile_array是未初始化的野指针,直接触发未定义行为,导致输出异常。
核心问题2:previous指针指向栈临时变量
每次循环创建的struct tile curr_tile是栈上局部变量,循环结束后内存会被回收。previous = &curr_tile会让结构体中的previous指针指向无效内存,后续使用会引发错误。
核心问题3:printf格式化符类型不匹配
type是char类型,却用了%lu(无符号长整型)格式化符;distance是size_t类型,对应格式化符应为%zu,类型不匹配导致输出异常。
修复后的代码
#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <assert.h> struct tile { bool visited; char type; // '#' / ' ' / 'X' / 'o' size_t distance; // distance in tile_array size_t row; size_t col; struct tile* previous; // pointer to previous tile }; struct maze { struct tile* tile_array; // 1D array of <tile> structs size_t length; // length of tile_array size_t *rows_lengths; // [row1_len, row2_len, ...] size_t row_count; // count of rows used }; bool illegal_input_char(char c) { return !(c == '#' || c == ' ' || c == 'X' || c == 'o' || c == '\n'); } bool maze_create(struct maze *maze, FILE *file) { size_t array_length = 4; // 直接将分配的数组关联到maze结构体 maze->tile_array = malloc(array_length * sizeof(struct tile)); if (maze->tile_array == NULL) { return false; } size_t rows_lengths_count = 4; maze->rows_lengths = malloc(rows_lengths_count * sizeof(size_t)); if (maze->rows_lengths == NULL) { free(maze->tile_array); maze->tile_array = NULL; return false; } char curr_char; size_t curr_distance = 0; size_t curr_row = 0; size_t curr_col = 0; struct tile *previous = NULL; while ((curr_char = fgetc(file)) != EOF) { assert(curr_distance <= array_length); assert(curr_row <= rows_lengths_count); // 扩容tile_array,直接操作maze的成员 if (curr_distance == array_length) { array_length += 4; struct tile *tmp = realloc(maze->tile_array, array_length * sizeof(struct tile)); if (tmp == NULL) { free(maze->tile_array); maze->tile_array = NULL; free(maze->rows_lengths); maze->rows_lengths = NULL; return false; } maze->tile_array = tmp; } // 扩容rows_lengths,直接操作maze的成员 if (rows_lengths_count == curr_row) { rows_lengths_count += 4; size_t *tmp = realloc(maze->rows_lengths, rows_lengths_count * sizeof(size_t)); if (tmp == NULL) { free(maze->tile_array); maze->tile_array = NULL; free(maze->rows_lengths); maze->rows_lengths = NULL; return false; } maze->rows_lengths = tmp; } // 处理换行逻辑 if (curr_char == '\n') { maze->rows_lengths[curr_row] = curr_col; curr_col = 0; curr_row++; previous = NULL; continue; } // 非法字符检查 if (illegal_input_char(curr_char)) { free(maze->tile_array); maze->tile_array = NULL; free(maze->rows_lengths); maze->rows_lengths = NULL; return false; } // 直接初始化数组中的tile,避免临时变量问题 maze->tile_array[curr_distance].visited = false; maze->tile_array[curr_distance].type = curr_char; maze->tile_array[curr_distance].distance = curr_distance; maze->tile_array[curr_distance].row = curr_row; maze->tile_array[curr_distance].col = curr_col; maze->tile_array[curr_distance].previous = previous; // 使用正确的格式化符 printf("WRITTEN CHAR: '%c', distance:%zu\n", maze->tile_array[curr_distance].type, maze->tile_array[curr_distance].distance); curr_col++; // 将previous指向数组中有效的tile地址 previous = &maze->tile_array[curr_distance]; curr_distance++; } // 处理文件末尾无换行的情况 if (curr_col > 0) { maze->rows_lengths[curr_row] = curr_col; curr_row++; } maze->length = curr_distance; maze->row_count = curr_row; return true; }
额外注意事项
- 确保
illegal_input_char函数正确过滤非法字符; - 处理文件末尾无换行的场景,避免最后一行长度未被记录;
- 调试用的
assert在发布版本建议替换为显式错误检查。
内容的提问来源于stack exchange,提问作者Loosos
相关产品推荐
相关产品推荐

