尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C语言经典题解析:星期判断的switch分支与缓冲区处理
刷C语言题的人十有八九都见过那套流传甚广的“C语言经典100例”第31题算得上其中最有教学浓度的一道小题。题目本身相当直白输入星期几英文单词的第一个字母判断它是星期几如果第一个字母对应多个结果就继续读第二个字母再判断。七个英文星期里M开头的只有MondayW开头的只有WednesdayF开头的只有Friday但T既能是Tuesday也能是ThursdayS既能是Saturday也能是Sunday所以这道题天然要求程序具备“二次判断”的能力。很多教程给出的参考答案都很短但照着敲完十有八九会在输入环节翻车——程序不等你输第二个字母就直接打印“输入错误”。这篇文章把题目背后的考点、两种能稳定运行的写法、新手最常踩的三个坑以及它怎么迁移到菜单程序和表驱动设计一次说清楚。刚学完分支结构、想找点题目练手的人花一个小时把这题真正啃透比连刷十道简单题值得多。1. 题目原型与真正的考点这道题不是“会switch”就行1.1 原题到底长什么样先把七个英文星期的全称摆出来Monday星期一Tuesday星期二Thursday星期四Wednesday星期三Friday星期五Saturday星期六Sunday星期日看首字母的分布情况M、T、W、F、S这五个字母覆盖了七天其中T和S各自对应两个星期这就是题干里“如果第一个字母一样则继续判断第二个字母”的来源。题目要求用户先输入一个字母程序根据这个字母决定要不要再要一个字母然后输出对应的中文星期。这道题在经典100例里的定位很有意思。它不需要数组、不需要指针、不需要递归甚至连循环都不用写前置知识只有分支结构和字符输入。但恰恰是这种看着简单的题才能把初学者在输入处理上的薄弱环节暴露得干干净净。1.2 先把判定表盘清楚写代码之前我习惯先把判定关系整理成一张表后面照着表翻译成switch分支就行了首字母第二个字母结果M / m不需要星期一T / tu星期二T / th星期四W / w不需要星期三F / f不需要星期五S / sa星期六S / su星期日其他-输入错误不知道你有没有发现题干里“第一个字母一样就继续判断第二个字母”这句话翻译成程序逻辑就是读第一个字符 → 用switch决定走哪条路线 → 在T和S这两条路线里再读一个字符 → 用if判断第二个字符。也就是说这题的代码结构并不是“一个大switch平铺到底”而是“外层switch 内部小if”的组合。把这张判定表画出来之后后面写case只是体力活。很多人一上来就抄代码抄完发现逻辑漏洞百出往往是因为跳过了“先列判定表”这一步。程序员的解题顺序应该是理解问题 → 结构化描述 → 翻译成代码而不是反过来。1.3 为什么这道题能进“经典100例”这题的题目编号虽然排在中间但它同时串联了三个关键知识点switch分支结构、单个字符读取、输入缓冲区的处理。大多数教材里的switch练习题都是“输入1、2、3选择菜单功能”像这样用字符做case的题目相对少见。而字符分支恰好是未来写命令行菜单、交互式小工具的标配写法。另外一个原因是它的二次判断逻辑。程序不是在拿到第一个字符后直接给结论而是要根据第一个字符的情况决定是否发起第二次输入。这种“流程控制取决于用户的输入内容”的交互模式和真实项目里的用户引导逻辑一脉相承。所以别看它只是几十行的小程序练完它能顺带理解很多命令行工具的设计思路。2. 题解思路推演分支结构从if-else到switch的取舍逻辑2.1 如果一上来用if-else链写会怎样很多人的第一反应是用if-else从头比到尾if (ch1 M) printf(星期一\n); else if (ch1 T) { // 继续判断第二个字母 } else if (ch1 W) printf(星期三\n); else if (ch1 F) printf(星期五\n); else if (ch1 S) { // 继续判断第二个字母 } else printf(输入错误\n);这段代码编译能过运行结果也基本正确但问题在于条件一多整个程序变成一长条读代码的人要不断把注意力拉回到每个if条件上才能确认逻辑链条是否完整。中间少写一个else某个分支就会悄悄漏掉而且很难一眼发现。switch的引入正是为了解决“一个值和多个常量做等值匹配”的场景。它把“比较”集中到一个表达式上然后把所有case像抽屉一样铺开谁匹配就进入哪个抽屉。语义上和if-else链等价但结构更直观。对这道题来说五个首字母分支一个default分支用switch放出来整个程序的走向一目了然。2.2 switch的匹配机制与字符case的写法要理解switch为什么适合这题得先弄清楚它在C语言里的匹配规则。switch后面括号里的表达式会被计算一次然后从上往下和每个case后面的常量做比较找到相等的那个就从那里进入执行。如果一路找不到相等的就走default。C语言里的字符在本质上是整数M这个字符对应的是ASCII码77。所以switch表达式可以用char类型case后面也可以写字符常量。这是一个让初学者感到奇怪、但用起来非常舒服的特性switch (ch1) { case M: ... break; case T: ... break; }这里必须强调break的作用。break会跳出整个switch结构如果某个case块末尾没有break程序不会停下来而是继续往下执行下一个case的代码这种行为叫fall-through穿透。你可以试着把M分支的break删掉然后输入M程序会接着执行T分支屏幕上一会儿让你输第二个字母一会儿打印星期三最终输出一串莫名其妙的结果。所以我的习惯是每个case块结尾第一件事就是先写break再回来补中间的代码这样就不会忘。等代码改到一半再临时加break是最容易漏的。2.3 第二个字母的判断为什么又回到了if既然switch这么好用要不要在T和S分支里再嵌套一层switch呢可以但没必要。因为T分支后面只有u和h两种可能S分支后面也只有a和u两种可能两三个候选用if-else写代码更短、读起来更直接if (ch2 u) printf(星期二\n); else if (ch2 h) printf(星期四\n); else printf(输入错误\n);只有候选数量很多而且每个候选都是“等值比较”的时候才值得用内层switch。如果某个分支后面有十几个候选字母那再开一层switch会让代码更整齐。选型经验就一句话switch适合“多个固定值的等值分发”if-else适合“少路分支或者带范围比较的场景”哪个能让读者一眼看懂就优先用哪个。3. 参考代码逐段拆解字符输入、缓冲区与返回值处理3.1 一份能稳定运行的参考代码网上很多版本的代码是用scanf配合char变量写的运行起来会遇到一个经典问题输入首字母后按回车程序不会等第二个字母而是直接判断失败。根因是缓冲区里残留的换行符被第二次scanf读走了。下面这份代码用getchar配合清空缓冲区的方法解决了这个问题同时用tolower统一了大小写#include stdio.h #include ctype.h int main(void) { int ch1, ch2; printf(请输入星期几英文单词的首字母); ch1 tolower(getchar()); while (getchar() ! \n) ; // 清空缓冲区内剩余字符 switch (ch1) { case m: printf(星期一\n); break; case t: printf(请输入第二个字母); ch2 tolower(getchar()); while (getchar() ! \n) ; if (ch2 u) printf(星期二\n); else if (ch2 h) printf(星期四\n); else printf(输入错误\n); break; case w: printf(星期三\n); break; case f: printf(星期五\n); break; case s: printf(请输入第二个字母); ch2 tolower(getchar()); while (getchar() ! \n) ; if (ch2 a) printf(星期六\n); else if (ch2 u) printf(星期日\n); else printf(输入错误\n); break; default: printf(输入错误\n); break; } return 0; }这份代码我实测过输入M、m、T、t、S、s都能按预期工作而且无论用户在字母后面多敲了几个字符多余的字符都会被清空循环吞掉不会影响下一次输入。建议在本地编译器里跑一遍再对照下面的拆解理解每一段的作用。3.2 关键代码段拆解getchar、清空缓冲区与大小写第一个值得注意的地方是变量的类型。我用了int ch1, ch2而不是char。原因在于getchar的返回值是int它不仅要表示读到的字符还要在输入结束时返回EOF通常是-1。如果用char接收某些平台上会把EOF截断成一个普通字符导致后续判断不可靠。初学者看到“明明读的是字符为什么要用int”时记住这一点就够了。第二个重点是清空缓冲区的写法while (getchar() ! \n) ;这个循环会持续从输入缓冲区读取字符直到读到的字符是换行符为止。这样做的目的是把用户按下回车后留在缓冲区里的内容全部消费掉。那单独一个分号是什么它是C语言里的空语句表示循环体什么都不做。有人觉得这种写法太隐晦那也可以写成while (getchar() ! \n) { // 丢弃缓冲区中的多余字符 }效果完全一样只是多了花括号看起来更明确。我在上面的参考代码里用了带空语句的简写因为这是很多老手习惯的写法熟悉它对阅读开源代码有帮助。第三个是大小写处理。我用了tolower函数把读到的字符统一转成小写这样switch里只需要写小写case。使用tolower时有一个规范问题它的参数要求是unsigned char能表示的值或者EOF。getchar返回的是int如果值来自用户输入的普通字符转成unsigned char是安全的所以代码里直接调用不会出问题。关于中文输出的乱码问题也顺便提一句在Windows的老式控制台里如果printf输出中文出现乱码可以把“星期一”这样的字符串临时改成“Monday”或者使用宽字符方案。这属于环境问题和算法本身无关。3.3 用测试用例驱动验证程序写完一定不要只测一组输入就收工。我建议先根据判定表手写几组输入和预期输出再跑程序对比。下面是我常用的测试用例输入1输入2预期输出M无星期一m无星期一Tu星期二th星期四W无星期三F无星期五Sa星期六su星期日X无输入错误Tx输入错误这十组用例覆盖了单字母分支、二字母分支、大小写、错误输入四条路径。特别是最后两组很多人会漏测只测正确的第二个字母不测错误的第二个字母。漏掉else分支的错误处理程序在遇到非法输入时会什么都不输出看起来像死机了一样。4. 实测翻车现场跑这道题最容易踩到的三个坑4.1 坑一缓冲区里残留的换行把第二次输入吃掉了这是这道题里翻车率最高的一个问题。还原一下场景用户输入M程序打印“请输入星期几英文单词的首字母”用户键入M并按下回车缓冲区里实际上存了两个字符M和\n。第一次调用getchar取走了M缓冲区里还躺着\n。如果代码在第二次输入时直接再调getchar或者scanf(%c)取走的就不是用户后来输入的第二个字母而是上一次遗留的换行符。程序拿到换行符判断它不等于u也不等于h立刻打印“输入错误”。网上流传的很多参考答案长这样scanf(%c, i); ... scanf(%c, j); // 这里会读走缓冲区的换行符运行之后的表现就是“程序不让我输第二个字母”。解决办法有两个。一是在每次读取字符后把缓冲区清干净也就是参考代码里的while(getchar() ! \n)二是在scanf的格式串里加一个前导空格scanf( %c, j);%c前面的那个空格含义是让scanf先跳过所有空白字符再读取。这样缓冲区里的换行符会被自动忽略程序会老老实实等待用户输入下一个字符。这里有个理解要点scanf家族里%d、%f这类格式会自动跳过前导空白而%c不会。用生活里的场景类比%d会在进门之前顺手扫掉门口的垃圾%c则是直接推门进去脚底粘着什么就带进什么。4.2 坑二大小写没有归一化如果程序只判断大写字母用户输了个小写mswitch会直接走进default分支。很多初学者会抱怨“为什么我的程序只能识别大写”原因就在这。处理方式有两种一种是在switch里同时列大写和小写case像这样case M: case m: printf(星期一\n); break;另一种是先归一化再分发比上一个办法省一半的重复代码ch1 tolower(ch1); switch (ch1) { case m: ... }我推荐第二种。tolower这个函数只处理单字符遇到 M 会返回m遇到非字母字符会原样返回。归一化之后switch里只需要处理小写情况代码结构干净得多。顺手说一句很多图形界面程序里处理用户输入“先统一大小写再比较”几乎是标配做法这道题练的就是这个思路。4.3 坑三漏写break导致case穿透第三个坑藏在switch的细节里。假设你写了T分支里面printf提示用户输入第二个字母但忘记在分支末尾加break。输入T之后程序执行完T分支不会停下来会继续向下执行W分支的printf再执行F分支再执行S分支……最后屏幕上会出现“请输入第二个字母”“星期三”“星期五”“请输入第二个字母”“星期三”“星期五”这种一连串输出。对付case穿透除了写代码时先补break再写逻辑还有一个检查技巧使用GCC或Clang编译时加上-Wimplicit-fallthrough警告选项编译器会明确提示哪些case疑似漏掉break。我用这个选项扫过不少初学者代码几乎每次都能扫出一两个漏网的break。这道题里更隐蔽的是内层if漏else如果用户输入了T第二个字母输的是x本来应该走到else打印“输入错误”结果因为只写了if没写else程序什么都不输出看起来就像没反应。这种“静默失败”在调试时最难定位所以内层判断一定要把兜底分支写全。5. 从星期判断出发字符驱动菜单与表驱动的进阶套路5.1 字符驱动菜单一个字符分发到一个功能把练习31的switch结构往实际项目方向推一步你会发现它就是一个极简的菜单驱动框架。很多命令行小工具都是这个套路打印菜单读取一个字符然后根据字符进入不同功能。骨架大概长这样while (1) { printf(A. 添加记录\n); printf(B. 删除记录\n); printf(C. 显示记录\n); printf(Q. 退出\n); printf(请选择); int c getchar(); while (getchar() ! \n) ; switch (c) { case A: case a: add_record(); break; case B: case b: delete_record(); break; case C: case c: show_record(); break; case Q: case q: return 0; default: printf(无效选项请重新输入\n); break; } }这段代码里getchar配清空缓冲区的组合是标配。为什么菜单程序也要清缓冲区因为用户输入“A”后按回车缓冲区里留着换行符如果不处理下一次循环的getchar会直接读到换行然后走default形成一个“每次都要按两下才有反应”的怪现象。我在第31题的参考代码里用到的清空手法在这个菜单循环里原封不动地复用了。5.2 当选项变多把switch换成一张表如果判断的对象从7个星期扩展到几十个城市代号、几十个错误码switch就会变得很长维护成本直线上升。这种时候更适合用表驱动把所有候选放在数组里用前缀匹配的算法去查表。下面是一个把星期查表实现出来的版本只依赖标准C函数可以本地直接编译运行#include stdio.h #include string.h #include ctype.h static const char *days[] { monday, tuesday, wednesday, thursday, friday, saturday, sunday }; static const char *week_cn[] { 星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期日 }; static void to_lower(char *s) { for (; *s; s) *s (char)tolower((unsigned char)*s); } int main(void) { char buf[64]; int i, len; printf(请输入星期的英文前缀例如 Mon、tue、sat); if (scanf(%63s, buf) ! 1) return 1; to_lower(buf); len strlen(buf); for (i 0; i 7; i) { if (strncmp(buf, days[i], len) 0) { printf(%s\n, week_cn[i]); return 0; } } printf(没有匹配的星期\n); return 0; }这个程序的判定逻辑不再依赖分支结构而是依赖数据的顺序。用户输入“Mon”时程序拿这个字符串和表中每一项的前三个字符做比较匹配到monday就输出星期一。输入“s”时saturday和sunday都以s开头按数组顺序会先匹配到saturday。这就引出一个新问题前缀匹配到多项时怎么处理对原题来说这正是T和S分支继续读第二个字母的原因。所以你可以把这个查表程序继续改造匹配到多个候选时提示用户继续输入再按更长的前缀缩小范围。这个思路其实就是搜索引擎里“输入提示”的雏形。5.3 顺着这个思路还能往哪里扩展练完第31题我建议你把它改成“输入任意长度的星期前缀返回对应星期几”的小工具然后再把工具改成“首字母联想程序”用户每输入一个字母就列出当前所有可能的匹配项。这个过程会让你真正理解“数据和逻辑分离”是什么意思——新增一个候选星期只需要往数组里加一行字符串而不需要改任何一个if或switch。从这个角度看那道看似简单的判断星期题练的其实是两套本事一是把输入逻辑处理干净二是把多分支改造成表驱动。这两件事在后续写课程设计、写小工具、甚至写真正的业务代码时都会反复用到。很多人刷题刷到后面觉得语法全都会了但写起项目还是无从下手缺的往往就是这种“把简单题目往工程方向推演”的训练。我个人的习惯是每练完一道小题就顺手把它改造成稍微通用一点的版本。第31题被我分别改过菜单框架和前缀查表两个小工具之后写命令行交互程序时很多结构几乎是原样搬过去的。如果你也在刷这100例建议别满足于把答案跑通动手把这道题改成读任意长度前缀再改成循环菜单收获会比连刷十道简单题大得多。
RELATED

相关推荐

利用预计算上下文,更快速、更低成本地开展支持问题调查

利用预计算上下文,更快速、更低成本地开展支持问题调查

作者:来自 Elastic Abhimanyu Anand 预计算上下文使 Elastic 的支持 agent 的输入 tokens 减少了 58%,延迟降低了 40%,通过减少重复检索,提高了支持问题调查的效率。 Agent Builder 现已正式发布(GA)。立即…

📅 2026/10/10 5:59:27
巴菲特投资体系实战拆解:能力圈、护城河与安全边际

巴菲特投资体系实战拆解:能力圈、护城河与安全边际

巴菲特这三个字,在投资圈几乎被说烂了。但有意思的是,我观察到的真实情况是:大部分人学巴菲特,学得四不像。有人把"长期持有"理解成被套牢之后死扛不动,有人把"不懂不做"理解成只敢买银行理财&…

📅 2026/10/10 5:59:27
Playwright自动化测试实战:从Selenium迁移到动态页面抓取

Playwright自动化测试实战:从Selenium迁移到动态页面抓取

记得第一次用 Selenium 写自动化脚本,被那个隐式等待和显式等待折磨得没脾气。页面加载快一点慢一点都不行,不是报找不到元素,就是等半天卡死。后来换到 Playwright,瞬间感觉这才是人该用的工具。这个项目火起来不是没道理&#x…

📅 2026/10/10 5:54:26
MORE NEWS

更多资讯

📰

2026年降AI率工具真实测评:10款改写工具的优劣与使用边界

最近后台被问到最多的一个问题,大概就是“2026年自考复习写的东西,AI率太高,怎么办”。这不是什么新鲜话题,从两年前开始,我就陆续测过三十多款和文本降重、降AI率有关的工具。这次干脆花了整整四周,把市面…

📰

BigBanana AI Director:一站式AI短剧与漫剧导演平台工程实践

简介:BigBanana AI Director 是一套面向短剧与漫剧创作者的工业级本地化 AI 制作平台,主打从故事构思到成片输出的一站式工作流,数据全程留在本机,兼顾隐私安全与知识产权归属。它整合剧本生成、角色设定、分镜设计、语音合成与画…

📰

BigBanana AI Director:工业级AI短剧与漫剧全流程制作实战指南

简介:BigBanana AI Director 是一套面向专业内容创作者的工业级 AI 短剧与漫剧全流程制作平台,基于开源架构构建,支持完全离线运行,从故事生成、角色设定、分镜设计、语音合成到成片输出一站式完成,数据全程保留在本地…

📰

深入理解Spring三级缓存:循环依赖的机制、局限与排查实践

1. 循环依赖是什么,以及你为什么会碰到它先聊个场景。有一次我在排查一个偶发启动失败的问题,某个服务明明本地跑得好好的,一上测试环境就报BeanCurrentlyInCreationException。日志堆栈指来指去,最后定位到就是两个 Service 互相…

📰

用编程思维打造个人知识体系:IoC、依赖注入与响应式学习实操指南

我前两年一度陷入一个很常见的循环:买了不少课、收藏了一堆文章、笔记记了好几本,可三个月后发现,真正能讲清楚、能上手用起来的,其实没几个。后来想明白一个问题——我不是懒,也不是笨,而是把学习做成了“…

📰

联邦学习赋能大模型WAF:破解数据孤岛与攻击变异难题

1. 项目概述:当大模型撞上WAF,不是堆算力,而是重新定义“看见攻击”的方式最近在某高校实验室做安全方向的模拟项目X时,团队里一位做NLP的同事随手把一份Web攻击日志喂给刚微调好的小规模语言模型,结果模型不仅标出了S…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬