C语言中第二次调用drawRoom函数后字符串无法在PowerShell打印的问题求助
嘿,兄弟!看了你的代码和问题描述,太懂你这种新手遇到诡异bug的抓狂感了——第一次能正常显示,后面就只剩空白,位置还对,这谁看了不懵😅。别担心,咱们一步步揪出问题根源,帮你搞定这个小游戏!
首先,最致命的bug:scanf 缓冲区溢出破坏栈内存
你现在用的是 scanf("%1s", &input);,但 input 是单个 char 类型变量,只有1字节的存储空间。而 %1s 格式符的行为是:读取1个字符,然后自动在后面追加一个空终止符 \0——这就麻烦了,\0 没地方放,只能写到 input 后面的栈内存里,直接造成缓冲区溢出!
这种溢出会随机破坏栈上的其他数据:可能是 playerX/playerY 的值(还好你说打印位置是对的,可能只是没破坏到这里),也可能会干扰到 drawRoom 函数里的局部变量,导致字符串初始化异常,最终打印出空白区域。
修复方法:
把 scanf 的调用改成:
scanf(" %c", &input);
%c专门用来读取单个字符,不会额外追加\0,刚好匹配input的char类型,不会溢出。- 前面的空格
是关键:它会自动跳过输入缓冲区里的空白字符(比如你每次输入后按的回车),避免下一次读取到回车当成有效输入。
其次,可选优化:避免栈上变长数组的潜在问题
你在 drawRoom 里用了变长数组 char drawString[currentRoom.size];,虽然C99支持,但栈内存的空间有限,而且局部栈变量容易被其他栈操作干扰(比如刚才的溢出就可能影响它)。换成堆内存动态分配会更安全:
int drawRoom(struct room currentRoom, int xPosition, int yPosition){ // 动态分配内存 char *drawString = malloc(currentRoom.size); if (drawString == NULL) { printf("内存分配失败!\n"); return 1; } // 原来的循环初始化逻辑不变 for(int loc = 0; loc < (currentRoom.size); ++loc){ int x = loc % (currentRoom.width + 1); int y = loc / (currentRoom.width + 1); if(y == currentRoom.height){ drawString[loc] = '\0'; continue; } if(x == currentRoom.width){ drawString[loc] = '\n'; continue; } if(y == 0 || y == currentRoom.height - 1){ drawString[loc] = '='; continue; } if(x == 0 || x == currentRoom.width - 1){ drawString[loc] = '|'; continue; } if(x == xPosition && y == yPosition){ drawString[loc] = 'P'; continue; } if(x == currentRoom.goalX && y == currentRoom.goalY){ drawString[loc] = 'G'; continue; } drawString[loc] = '.'; } printf("%s\n", drawString); // 用完记得释放内存,避免内存泄漏 free(drawString); return 0; }
这样分配的内存来自堆,不会被栈上的其他变量干扰,稳定性会好很多。
顺便解决你提到的“多个输入”小问题
刚才改的 scanf(" %c", &input); 其实已经解决了这个问题——前面的空格会跳过回车,不会让下一次读取到无效的回车字符,你每次输入WASD后按回车,程序只会读取到你输入的字母,不会把回车当成下一次的输入。
最后,验证你的逻辑
你在 drawRoom 里的字符串初始化逻辑是对的:每次调用都会重新创建并初始化整个字符串,不存在“修改现有字符串”的问题,所以只要修复了上面的bug,第二次调用应该就能正常显示了!
赶紧试试这些修改,应该就能看到你的小角色正常移动啦😎!
内容来源于stack exchange

