C语言函数修改二维数组后程序挂起求助(井字棋游戏)
解决井字棋程序输入后挂起及数组访问错误问题
你的程序出现挂起和数组写入问题,核心是类型不匹配、逻辑错误、输入处理缺陷共同导致的,以下是具体问题分析和修复方案:
核心问题分析
二维数组与
char**类型不兼容
主函数中board是char[3][3]二维数组,退化为指针时是char (*)[3](指向长度为3的char数组的指针),但makeMove函数参数是char**,两种类型无法直接转换。直接传递会导致指针解析错误,写入board[answerY][answerX]时访问非法内存,引发崩溃或挂起。循环内重复初始化棋盘
char board[3][3]; memset(board, ' ', 9);放在while循环内部,每轮循环都会重置棋盘,玩家之前的落子全部丢失,游戏逻辑完全失效。输入缓冲区过小导致残留数据
moveX和moveY大小仅为2,当用户输入数字加回车(如1\n)刚好占满,但输入多字符(如12\n)时,剩余字符会留在输入缓冲区,导致下一次fgets直接读取残留内容,引发死循环(挂起)。无效输入无提示
getNum函数中,输入非数字、多余字符时没有提示用户重新输入,用户会误以为程序卡住。
修复步骤
1. 修正函数参数类型,匹配二维数组
将makeMove的参数改为兼容二维数组的类型,两种写法任选其一:
// 写法1:明确指向长度为3的char数组的指针 int makeMove(char (*board)[3], char* moveX, char* moveY, char playerMove) { // 原有逻辑保留 } // 写法2:更直观的二维数组声明 int makeMove(char board[3][3], char* moveX, char* moveY, char playerMove) { // 原有逻辑保留 }
2. 棋盘初始化移到循环外部
确保棋盘在整个游戏过程中只初始化一次,保留玩家落子:
int main() { int turns = 0; const int MAX_TURNS = 9; char playerOneChar = 'X'; char playerTwoChar = 'O'; char currentChar = playerOneChar; // 棋盘初始化移到循环外,仅执行一次 char board[3][3]; memset(board, ' ', 9); // 修正循环条件:9轮后游戏结束,用<而非<= while (turns < MAX_TURNS) { // 增大缓冲区避免溢出,初始化时用0清空 char moveX[10]; memset(moveX, 0, sizeof(moveX)); char moveY[10]; memset(moveY, 0, sizeof(moveY)); int result = makeMove(board, moveX, moveY, currentChar); if (result == 0) { // 新增:打印当前棋盘,方便玩家查看进度 printf("\n当前棋盘:\n"); for (int i = 0; i < 3; i++) { printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]); if (i != 2) printf("---+---+---\n"); } // 切换玩家 currentChar = (currentChar == playerOneChar) ? playerTwoChar : playerOneChar; turns++; } else if (result == -1) { printf("输入越界,请输入1-3之间的数字。\n"); } else if (result == -2) { // 后续新增的"位置已占用"错误处理 } } printf("游戏结束!\n"); }
3. 优化输入处理:增大缓冲区+错误提示
修改getNum函数,添加无效输入提示,并处理换行符:
long getNum(char* buffer, int bufferSize) { long answer; int success = 0; while (!success) { if (!fgets(buffer, bufferSize, stdin)) { return 1; } // 移除换行符,避免后续解析问题 char* newline = strchr(buffer, '\n'); if (newline) *newline = '\0'; char* endptr; errno = 0; answer = strtol(buffer, &endptr, 10); if (errno == ERANGE) { printf("数字超出范围,请输入1-3之间的数字:"); success = 0; } else if (endptr == buffer) { printf("请输入有效的数字:"); success = 0; } else if (*endptr != '\0') { printf("输入包含无效字符,请输入纯数字:"); success = 0; } else { success = 1; } } return answer; }
4. 额外优化:检查落子位置是否已被占用
在makeMove中添加位置占用判断,避免重复落子:
int makeMove(char board[3][3], char* moveX, char* moveY, char playerMove) { printf("Make your x move: "); int answerX = (int)getNum(moveX, 10); printf("Make your y move: "); int answerY = (int)getNum(moveY, 10); answerX--; answerY--; // 合并越界判断 if ((answerX < 0 || answerX > 2) || (answerY < 0 || answerY > 2)) { return -1; } // 检查位置是否已被占用 if (board[answerY][answerX] != ' ') { printf("该位置已有棋子,请重新选择。\n"); return -2; // 用不同返回值区分错误类型 } board[answerY][answerX] = playerMove; return 0; }
内容的提问来源于stack exchange,提问作者ProgrammingSolver
相关产品推荐
相关产品推荐

