Tic-tac-toe游戏存档加载异常:无法从TXT文件读取棋盘状态
井字棋游戏存档加载问题修复
我正在开发一款Tic-tac-toe(井字棋)游戏,支持根据用户输入打印棋盘,目前已实现棋盘状态、网格尺寸及回合数的保存功能,但从TXT文件加载数据时,棋盘状态无法成功读取。
原FileLoad函数代码
void FileLoad(char ***Board, int *x, int *y, int *roundcount, const char *filename) { FILE *Load =fopen(filename, "r"); if (Load == NULL) { printf("Error"); return; } fscanf(Load, "%d", *roundcount); fgetc(Load); fscanf(Load, "%d %d ", *x, *y); *Board = (char **)malloc(*y * sizeof(char *)); (*Board)[0] = (char *)malloc((*y * *x) * sizeof(char)); for (int i = 1; i < *y; ++i) (*Board)[i] = (*Board)[0] + i * *x; fgetc(Load); for (int i = 0; i < *y; ++i) { for (int j = 0; j < *x; ++j) fscanf(Load, "%c", &(*Board)[i][j]); fgetc(Load); } fclose(Load); }
调试后确认问题出在最后读取棋盘状态的fscanf代码段,以下是5*5网格的存档文件示例:
2 5 5 X O
(注:存档中每行对应棋盘的一行,包含空格、X/O字符)
问题分析与修复方案
1. fscanf参数错误(核心问题)
原代码中读取整数时传递的是变量值而非地址:
fscanf(Load, "%d", *roundcount);错误,*roundcount是int类型的值,fscanf需要的是变量的指针地址,应改为fscanf(Load, "%d", roundcount);- 同理,
fscanf(Load, "%d %d ", *x, *y);改为fscanf(Load, "%d %d", x, y);,同时去掉格式串末尾的空格,fscanf会自动跳过空白字符(换行、空格等)
2. 多余的fgetc调用导致字符丢失
原代码中手动调用fgetc跳过换行符,但fscanf读取整数后会自动跳过后续空白,手动调用会误读棋盘的第一个有效字符,需移除这些多余的fgetc,仅保留一次跳过尺寸读取后剩余换行的操作。
3. 棋盘读取逻辑错误
用fscanf("%c")会读取所有空白字符(包括换行),导致棋盘行列错位。改用fgets读取每行数据,确保每行对应棋盘的一行,再将字符复制到棋盘数组中。
修改后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> void FileLoad(char ***Board, int *x, int *y, int *roundcount, const char *filename) { FILE *Load = fopen(filename, "r"); if (Load == NULL) { printf("Error opening file"); return; } // 读取回合数,检查读取是否成功 if (fscanf(Load, "%d", roundcount) != 1) { printf("Error reading round count"); fclose(Load); return; } // 读取网格尺寸x和y if (fscanf(Load, "%d %d", x, y) != 2) { printf("Error reading board dimensions"); fclose(Load); return; } // 分配棋盘内存 *Board = (char **)malloc(*y * sizeof(char *)); if (*Board == NULL) { printf("Memory allocation error"); fclose(Load); return; } (*Board)[0] = (char *)malloc((*y * *x) * sizeof(char)); if ((*Board)[0] == NULL) { free(*Board); printf("Memory allocation error"); fclose(Load); return; } for (int i = 1; i < *y; ++i) { (*Board)[i] = (*Board)[0] + i * *x; } // 跳过尺寸读取后剩余的换行符 char dummy; while ((dummy = fgetc(Load)) != '\n' && dummy != EOF); // 逐行读取棋盘数据 char line[100]; // 可根据最大网格尺寸调整数组大小 for (int i = 0; i < *y; ++i) { if (fgets(line, sizeof(line), Load) == NULL) { printf("Error reading board line %d", i); // 释放已分配内存避免泄漏 free((*Board)[0]); free(*Board); fclose(Load); return; } // 去掉行尾的换行符 size_t len = strlen(line); if (len > 0 && line[len-1] == '\n') { line[len-1] = '\0'; } // 将行内容复制到棋盘,不足部分用空格填充 for (int j = 0; j < *x; ++j) { (*Board)[i][j] = (j < strlen(line)) ? line[j] : ' '; } } fclose(Load); }
额外优化说明
- 增加了错误检查逻辑,避免文件格式错误或内存分配失败导致程序崩溃
- 处理了存档行长度不足的情况,用空格填充确保棋盘每个位置都有有效字符
- 保留了原代码的连续内存分配方式,避免碎片化
内容的提问来源于stack exchange,提问作者Moppity
相关产品推荐
相关产品推荐

