You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言动态分配未知尺寸2D游戏棋盘char数组的实现问题问询

方案选择结论

更推荐使用固定初始大小缓冲区+必要时动态扩容的逐行读取方案,比逐字符读取的实现效率更高,逻辑也更简洁:

  • 逐字符读取需要频繁调用IO接口(即使有系统缓存兜底),还要手动处理行计数、换行识别、缓冲区维护,冗余逻辑更多
  • 逐行扩容的方案可以直接复用标准库的fgets能力,仅在单行超长时触发扩容,性能和可维护性都更优

现有代码的问题修复

你目前的代码存在几处明显bug,会直接导致内存越界、运行崩溃:

  1. buf += needed_bytes_col 逻辑错误:realloc返回的是新缓冲区的首地址,直接向后偏移整个缓冲区长度后,下次fgets会写入未申请的内存空间,正确做法是记录当前行已读取的字节数,下次从对应偏移位置写入
  2. board = realloc(board, needed_num_rows) 内存大小计算错误:应该为needed_num_rows * sizeof(char*),否则分配的内存远小于指针数组需要的空间,会触发越界访问
  3. 未处理行尾的换行符\n:读取到的行末尾如果带换行符,会被存入棋盘数组,成为无效单元格
  4. 循环结束后未释放空的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 11:57:03