C语言动态分配未知尺寸2D游戏棋盘char数组的实现问题问询
方案选择结论
更推荐使用固定初始大小缓冲区+必要时动态扩容的逐行读取方案,比逐字符读取的实现效率更高,逻辑也更简洁:
- 逐字符读取需要频繁调用IO接口(即使有系统缓存兜底),还要手动处理行计数、换行识别、缓冲区维护,冗余逻辑更多
- 逐行扩容的方案可以直接复用标准库的
fgets能力,仅在单行超长时触发扩容,性能和可维护性都更优
现有代码的问题修复
你目前的代码存在几处明显bug,会直接导致内存越界、运行崩溃:
buf += needed_bytes_col逻辑错误:realloc返回的是新缓冲区的首地址,直接向后偏移整个缓冲区长度后,下次fgets会写入未申请的内存空间,正确做法是记录当前行已读取的字节数,下次从对应偏移位置写入board = realloc(board, needed_num_rows)内存大小计算错误:应该为needed_num_rows * sizeof(char*),否则分配的内存远小于指针数组需要的空间,会触发越界访问- 未处理行尾的换行符
\n:读取到的行末尾如果带换行符,会被存入棋盘数组,成为无效单元格 - 循环结束后未释放空的
buf缓冲区,会出现内存泄漏
修正后的参考实现
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { FILE* level_file = fopen("level.txt", "r"); if (!level_file) { perror("打开关卡文件失败"); return 1; } int row_cap = 1; // 行指针数组容量 int row_cnt = 0; // 已读取行数 char** board = malloc(row_cap * sizeof(char*)); if (!board) { perror("分配棋盘内存失败"); fclose(level_file); return 1; } int col_buf_cap = 16; // 单行缓冲区初始容量 char* buf = malloc(col_buf_cap); if (!buf) { perror("分配行缓冲区失败"); free(board); fclose(level_file); return 1; } int read_offset = 0; // 当前行已读取的偏移量 while (fgets(buf + read_offset, col_buf_cap - read_offset, level_file) != NULL) { int read_len = strlen(buf + read_offset); // 判断是否读取到完整行 if (read_len > 0 && buf[read_offset + read_len - 1] == '\n') { // 去掉换行符 buf[read_offset + read_len - 1] = '\0'; // 行指针数组扩容判断 if (row_cnt >= row_cap) { row_cap = row_cap * 2 + 1; char** new_board = realloc(board, row_cap * sizeof(char*)); if (!new_board) { perror("扩容棋盘失败"); free(buf); free(board); fclose(level_file); return 1; } board = new_board; } // 存储当前行,直接复用buf的内存 board[row_cnt++] = buf; // 重置缓冲区参数,准备下一行读取 col_buf_cap = 16; buf = malloc(col_buf_cap); if (!buf) { perror("分配新行缓冲区失败"); return 1; } read_offset = 0; } else { // 单行未读完,扩容缓冲区 col_buf_cap *= 2; char* new_buf = realloc(buf, col_buf_cap); if (!new_buf) { perror("扩容行缓冲区失败"); free(buf); free(board); fclose(level_file); return 1; } buf = new_buf; read_offset += read_len; } } // 释放最后未使用的buf free(buf); fclose(level_file); // 后续可以统一将所有行补空格到最长行的长度,方便后续棋盘逻辑处理 int max_col = 0; for (int i = 0; i < row_cnt; i++) { int len = strlen(board[i]); if (len > max_col) max_col = len; } for (int i = 0; i < row_cnt; i++) { int len = strlen(board[i]); if (len < max_col) { char* new_row = realloc(board[i], max_col + 1); if (!new_row) { perror("补全行长度失败"); return 1; } memset(new_row + len, ' ', max_col - len); new_row[max_col] = '\0'; board[i] = new_row; } } // 此处可正常使用board数组,row_cnt为行数,max_col为列数 // 程序结束前释放内存 for (int i = 0; i < row_cnt; i++) { free(board[i]); } free(board); return 0; }
内容的提问来源于stack exchange,提问作者never_feel_mellow
相关产品推荐
相关产品推荐

