恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
C语言项目实战第三弹:输入校验与区间提示,完善你的猜数字游戏
首页
资讯中心
/
C语言项目实战第三弹:输入校验与区间提示,完善你的猜数字游戏
C语言项目实战第三弹:输入校验与区间提示,完善你的猜数字游戏
发布时间:2026/10/12 4:44:00
输入字母就死循环加上输入校验和区间提示才算真正好用的猜数字游戏在前两弹中我们基于游戏的核心逻辑初步实现了游戏的基础版接着我们通过函数的调用与封装先后完成了游戏功能的扩展和程序结构的优化。但到目前为止仍有一些功能尚未实现代码中也还存在漏洞这些问题都可能导致程序异常甚至崩溃。接下来我们将针对这些漏洞对代码进行修正或重构同时我们会先将新增功能用代码实现调试通过后再分别封装起来。第二弹结尾我们总结了几个待解决的问题其中最关键的两个是输入校验不完整玩家输入字母如a时scanf读取失败残留字符留在缓冲区导致死循环。缺少区间提示只提示“太大”或“太小”玩家无法有效缩小猜测范围。本弹我们就先解决这两个问题让游戏真正好用起来。一、输入校验让程序不再死循环1. 问题复现在原代码中所有输入都直接使用scanf(%d, var);。如果玩家输入字母scanf会返回 0并且字母会留在输入缓冲区中下一次循环仍然读取到同一个字母于是无限循环打印错误提示。2. 原理分析scanf的返回值是成功读取并赋值的项数而不是读取到的值本身。成功读取一个整数返回1遇到字母、符号等无法转换为整数的字符返回0遇到文件结束符EOF返回-1。当我们从键盘输入内容时数据先进入输入缓冲区然后scanf再从缓冲区中按格式读取。以输入a后按回车为例缓冲区里是a \n。scanf尝试把a解释为整数失败返回 0但没有从缓冲区中取走a。进入下一次循环时scanf又看到缓冲区里的a再次失败如此反复程序陷入死循环。3. 解决方案核心思路有两条检查scanf的返回值判断本次读取是否成功清空输入缓冲区把残留的非法字符扔掉。完整处理模板if(scanf(%d,var)!1){printf(输入无效请输入数字\n);while(getchar()!\n);// 清空缓冲区continue;// 重新输入}注意while (getchar() ! \n);最好写成同时检查 EOF更安全intc;while((cgetchar())!\nc!EOF);我们可以将清空缓冲区的逻辑封装成一个函数voidclear_buffer(){intc;while((cgetchar())!\nc!EOF);}4. 改造位置1main()中读取菜单选项do{menu();if(scanf(%d,input)!1){printf(输入无效请输入数字\n);clear_buffer();input-1;// 设为非0值保证循环继续不能设为0否则会退出循环continue;}switch(input){...}}while(input);2DifficultySelection()中读取难度while(1){if(scanf(%d,select)!1){printf(输入无效请输入数字\n);clear_buffer();selectmenu();// 重新显示难度菜单continue;}switch(select){...}}建议把selectmenu();直接放在do-while循环内部这样每次循环都会重新显示菜单代码更简洁。3DifficultySelection()中读取自定义次数case4:while(1){printf(请输入允许的猜测次数\n);if(scanf(%d,count)!1){printf(输入无效请输入数字\n);clear_buffer();continue;}// 检查是否有多余字符如小数点intcgetchar();if(c!\nc!EOF){printf(输入无效请输入整数\n);clear_buffer();continue;}if(count0){printf(次数必须为正整数请重新输入。\n);continue;}returncount;}4game()中读取猜测数字printf(请输入你猜测的数字\n);if(scanf(%d,guess)!1){printf(输入无效请输入数字\n);clear_buffer();continue;// 不计入次数}attempts;// 输入合法后才计次二、区间提示让玩家知道范围1. 思路维护两个变量low和high表示当前目标数字可能的范围。初始为1和100。每次猜测后如果猜大了说明目标数字小于guess更新high guess - 1如果猜小了说明目标数字大于guess更新low guess 1提示玩家当前有效范围。同时如果玩家输入的数字不在当前有效范围内提示重新输入且不计次数。2. 代码实现intlow1,high100;// 初始范围// 检查是否在有效范围内if(guesslow||guesshigh){printf(您输入的数字不在当前有效范围内请重新输入。\n);continue;// 不计次数}attempts;remainingcount-attempts;used_time(double)(clock()-start)/CLOCKS_PER_SEC;if(guesstarget){highguess-1;printf(抱歉你猜的数字太大了。\n);printf(已用次数%d\n,attempts);printf(剩余次数%d\n,remaining);printf(当前有效范围%d ~ %d\n,low,high);printf(用时%.2f秒\n,used_time);}elseif(guesstarget){lowguess1;printf(抱歉你猜的数字太小了。\n);printf(已用次数%d\n,attempts);printf(剩余次数%d\n,remaining);printf(当前有效范围%d ~ %d\n,low,high);printf(用时%.2f秒\n,used_time);}else{printf(恭喜你猜对了\n);printf(已用次数%d\n,attempts);printf(用时%.2f秒\n,used_time);break;}三、记录最佳成绩单次运行1. 思路在main()中定义best_attempts和best_time初始化为极大值。每局结束后如果猜对且成绩更好则更新并提示。2. 实现方式为了在game()中更新这些变量简单起见可以使用全局变量intbest_attempts9999;doublebest_time9999.0;然后在game()猜对时if(attemptsbest_attempts){best_attemptsattempts;best_timeused_time;printf( 新纪录最少次数%d\n,best_attempts);}每次开始游戏前可以在menu()中或main()中显示历史最佳printf(历史最佳%d次 / %.0f秒\n,best_attempts,best_time);注意全局变量虽然简单但会污染命名空间。更规范的做法是用指针参数或结构体我们会在后续文章中介绍。四、让难度梯度更明显1. 思路目前所有难度都是 1~100只是次数不同。我们可以让不同难度对应不同的数字范围难度数字范围次数上限简单1 ~ 5015 次普通1 ~ 10010 次困难1 ~ 2005 次自定义玩家自己定玩家自己定2. 实现方式全局变量定义全局变量max_range在难度选择中修改它intmax_range100;// 默认值intDifficultySelection(){intselect0;intcount0;selectmenu();while(1){if(scanf(%d,select)!1){printf(输入无效请输入数字\n);clear_buffer();selectmenu();continue;}switch(select){case1:max_range50;return15;case2:max_range100;return10;case3:max_range200;return5;case4:// 自定义模式让玩家输入次数和范围// ...输入校验略returncount;default:printf(输入错误请重新输入。\n);break;}}}在game()中intcountDifficultySelection();inttargetrand()%max_range1;// 使用全局变量这样难度梯度就真正实现了范围越大猜的空间越大越难猜中而次数的减少进一步提升了挑战性。五、将项目拆分成多个文件到目前代码已经超过 100 行全部堆在一个文件中不利于维护和扩展。我们可以根据功能拆分成game.h函数声明、宏定义game.c游戏逻辑实现main.c主函数只负责调用示例// game.h#ifndefGAME_H#defineGAME_Hvoidmenu();voidselectmenu();intDifficultySelection();voidgame();#endif// game.c#includegame.h#includestdio.h#includetime.h#includestdlib.h// ... 函数实现 ...// main.c#includegame.hintmain(){// ... 调用 ...return0;}多文件编程能让代码结构更清晰也便于团队协作。由于篇幅限制这里只给出框架完整代码可到 Gitee 仓库查看。六、小结与下一步通过本弹的修改我们解决了输入字母导致死循环的问题缺少区间提示的问题单次运行内记录最佳成绩难度梯度不明显的问题初步引入了多文件编程。但仍有一些可以继续优化的地方最佳成绩只在单次运行内有效关掉程序就丢失了需要文件操作持久化输入校验还可以更健壮比如处理小数、超长输入等多文件编程只给出了框架尚未完整实现代码中仍有重复的清空缓冲区代码可以进一步封装。这些我们会在下一弹中继续完善。源码获取与专栏说明为了方便大家对比各个阶段的代码实现学长把每一阶段的代码都整理并传到了 Gitee 仓库。 Gitee 仓库地址点击跳转遇到任何问题或者代码有可以优化的地方欢迎在仓库提 Issue 哦如果觉得有帮助点个 Star 鼓励一下学长吧~下一篇预告我们将把游戏成绩保存到文件让最佳纪录持久化同时完善多文件编程并进一步优化输入校验和代码结构。下一篇《C语言项目实战第四弹成绩持久化与多文件编程打造更专业的猜数字游戏》敬请期待