C语言中for循环未修改变量mistake却自动变为0的问题排查
问题原因分析
1. 缓冲区溢出直接篡改mistake值
你的代码中char password[6]仅分配6字节空间,但gets()函数完全不检查输入长度。当输入的密码超过5个字符(字符串需要1字节存储\0结束符),超出部分会越界写入栈上password后方的内存区域——而mistake变量恰好位于这个位置,越界的字符会直接覆盖mistake的内存值,导致它被意外改成0。
比如输入123456(6个字符),password的6个位置存满字符后,字符串结束符\0会被写到第7个字节,也就是mistake的内存起始位置,直接将mistake的低字节置0,最终变量值变为0。
2. 输入缓冲区残留换行符引发逻辑异常
gets(password)读取密码后,输入的换行符会留在标准输入缓冲区。后续scanf("%d\n", &mistake)中的\n会让scanf持续等待非空白字符,直到输入数字后再输入额外空白才返回;进入循环后,gets(guess)会先读取缓冲区残留的换行符,导致guess被设为空字符串,后续字符比较逻辑完全混乱。
3. 其他潜在问题
count变量未在每次循环后重置,第一次验证失败后的累计值会带到下一次循环,导致后续即使输入正确密码,也可能因count不等于6无法触发正确提示。- 直接硬比较6个字符,若输入密码长度不足6,数组后续字节为随机垃圾值,会导致比较结果错误。
gets()是C标准已废弃的不安全函数,无任何缓冲区溢出防护,是代码隐患的核心根源。
解决方案
以下是修复后的完整代码,同时解决所有问题:
#include <stdio.h> #include <string.h> int main() { // 分配足够空间,留足字符串结束符位置,限制输入长度避免溢出 char password[10]; // 用fgets替代gets,安全读取输入 if (fgets(password, sizeof(password), stdin) == NULL) { return 1; } // 去掉fgets读取到的换行符,保证字符串格式正确 password[strcspn(password, "\n")] = '\0'; // 校验密码长度(匹配原代码逻辑,要求5个字符) if (strlen(password) != 5) { printf("密码必须是5个字符\n"); return 1; } int mistake = 0; // 清理缓冲区残留的换行符 int c; while ((c = getchar()) != '\n' && c != EOF); printf("请输入尝试次数:"); // 校验尝试次数输入有效性 if (scanf("%d", &mistake) != 1 || mistake <= 0) { printf("输入无效的尝试次数\n"); return 1; } // 清理scanf后的换行符,避免影响后续读取 while ((c = getchar()) != '\n' && c != EOF); char guess[10]; for(int i = 0; i < mistake; i++) { printf("请输入密码:"); if (fgets(guess, sizeof(guess), stdin) == NULL) { return 1; } guess[strcspn(guess, "\n")] = '\0'; // 先校验输入长度 if (strlen(guess) != 5) { printf("密码长度错误\n"); if (i == mistake - 1) { printf("you were electrocuted\n"); return 0; } continue; } // 用strcmp直接比较字符串,替代手动循环,避免垃圾值干扰 if (strcmp(password, guess) == 0) { printf("correct\n"); return 0; } else { if (i == mistake - 1) { printf("you were electrocuted\n"); return 0; } else { printf("wrong password\n"); } } } return 0; }
关键修复点
- 用
fgets()替代gets(),通过第二个参数限制读取长度,彻底杜绝缓冲区溢出。 - 用
strcspn()去除fgets()读取到的换行符,保证字符串格式正确。 - 每次输入后清理标准输入缓冲区的残留字符,避免后续读取操作出错。
- 用
strcmp()直接比较字符串,替代手动循环比较,逻辑更简洁且避免垃圾值干扰。 - 增加输入有效性校验(密码长度、尝试次数合法性),提升代码健壮性。
内容的提问来源于stack exchange,提问作者Jack C
相关产品推荐
相关产品推荐

