C语言if-else+strcmp实现落子功能时陷入错误循环问题排查
问题核心原因
代码出现随机列值、持续判定输入非法的核心原因是字符串缓冲区溢出,破坏了栈上相邻变量的内存数据。
C语言的字符串必须以\0作为结束标记,你一开始定义的char p1move[2]长度只有2字节,而"A1""B2"这类合法坐标包含2个可见字符,加上结束符总共需要3字节存储空间。用scanf("%s", p1move)读取输入时,函数会自动在输入内容末尾追加\0,多出来的1字节会直接越界写到栈上和p1move相邻的变量内存上——刚好column_move就挨着这个数组,所以写入的\0和后续越界读取的随机内存值会直接把column_move改成4196901这类无意义的大数;同时因为数组里没有正确的结束符,strcmp对比时会越界读取数组后方的随机内存内容,经常返回非0值,导致程序误判输入非法。你之前调整数组长度后问题依旧,大概率是只把长度设为2,依然没有为字符串结束符预留空间,溢出问题没有被解决。
其他隐藏逻辑bug
- 每次do-while循环开始时没有重置
invalid标记为0:只要用户第一次输入非法值把invalid设为1,后续哪怕输入正确坐标,invalid还是保持1,会一直判定输入非法。 - C3坐标的下标赋值错误:原代码中C3的赋值是
row_move = 1; column_move = 2;,和C2的下标完全一致,按照棋盘左上角为(0,0)的规则,C3正确对应下标应该是第0行第2列。 - do-while循环的终止条件错误:原循环条件仅判断棋盘位置是否为空,没有把输入非法的场景纳入判断,出现非法输入时可能因为随机下标越界访问内存直接触发崩溃。
- 原
scanf没有加读取长度限制,用户输入过长内容时会直接触发缓冲区溢出。
修正后的可运行代码
#define PLAYER1 'X' // 假设board为全局初始化的3*3棋盘数组,所有位置初始值为空格 char board[3][3]; void Player1Move() { // 缓冲区留足冗余长度,避免输入溢出 char p1move[16]; int row_move, column_move, invalid; printf("It's Player 1's turn!\n"); printf("What's your move?\n"); do { // 每次循环开头重置标记和下标值,避免上次循环的残留值影响判断 row_move = 0; column_move = 0; invalid = 0; printf("Enter your move "); // 加读取长度限制,最多读取15个字符,预留1字节存字符串结束符 scanf("%15s", p1move); printf("YOUR MOVE IS %s\n", p1move); if(strcmp(p1move,"A1") == 0) { row_move = 2; column_move = 0; } else if(strcmp(p1move, "A2") == 0) { row_move = 1; column_move = 0; } else if(strcmp(p1move, "A3") == 0) { row_move = 0; column_move = 0; } else if(strcmp(p1move, "B1") == 0) { row_move = 2; column_move = 1; } else if(strcmp(p1move, "B2") == 0) { row_move = 1; column_move = 1; } else if(strcmp(p1move, "B3") == 0) { row_move = 0; column_move = 1; } else if(strcmp(p1move, "C1") == 0) { row_move = 2; column_move = 2; } else if(strcmp(p1move, "C2") == 0) { row_move = 1; column_move = 2; } else if(strcmp(p1move, "C3") == 0) { // 修正C3对应的行下标 row_move = 0; column_move = 2; } else { invalid = 1; } /* 校验输入合法性和棋盘位置是否空闲 */ if(invalid == 1 || board[row_move][column_move] != ' ') { printf("That is not a valid space, please pick a new space\n"); } else { board[row_move][column_move] = PLAYER1; break; } // 修正循环条件:只要输入非法、或者选中位置已被占用,就持续要求用户重新输入 } while(invalid == 1 || board[row_move][column_move] != ' '); }
内容的提问来源于stack exchange,提问作者Derpy Dodo
相关产品推荐
相关产品推荐

