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

C语言函数修改二维数组后程序挂起求助(井字棋游戏)

解决井字棋程序输入后挂起及数组访问错误问题

你的程序出现挂起和数组写入问题,核心是类型不匹配、逻辑错误、输入处理缺陷共同导致的,以下是具体问题分析和修复方案:

核心问题分析

  1. 二维数组与char**类型不兼容
    主函数中board是char[3][3]二维数组,退化为指针时是char (*)[3](指向长度为3的char数组的指针),但makeMove函数参数是char**,两种类型无法直接转换。直接传递会导致指针解析错误,写入board[answerY][answerX]时访问非法内存,引发崩溃或挂起。

  2. 循环内重复初始化棋盘
    char board[3][3]; memset(board, ' ', 9);放在while循环内部,每轮循环都会重置棋盘,玩家之前的落子全部丢失,游戏逻辑完全失效。

  3. 输入缓冲区过小导致残留数据
    moveX和moveY大小仅为2,当用户输入数字加回车(如1\n)刚好占满,但输入多字符(如12\n)时,剩余字符会留在输入缓冲区,导致下一次fgets直接读取残留内容,引发死循环(挂起)。

  4. 无效输入无提示
    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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 00:14:54