ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C语言分支与循环全解析:从if-else到while与do-while

C语言分支与循环全解析:从if-else到while与do-while 很多初学C语言的朋友学到“分支和循环”这一章时会有一种很微妙的卡壳感。前面的变量、数据类型、printf和scanf怎么都是“写一行、执行一行”到了if和while这里代码突然“不听话”了——有时候部分语句不执行有时候又反复执行。我当年也在这个位置卡过不少时间后来才意识到分支和循环不是两个孤立的新知识点它是你从“写命令”升级到“写程序”的第一道分水岭。这篇是C语言系列第3篇的上半部分我打算把分支结构if、else if、switch和循环里最基础的两个兄弟while、do-while一次讲透配合可复现的代码示例。内容适合完全零基础的初学者也适合学过一遍但感觉逻辑没理顺、想重新梳理一遍的人。学完之后你应该能自己写出“输入一个成绩判断等级”“用菜单循环操作”这类带点交互逻辑的小程序。1. 从“直线代码”到“会拐弯的程序”为什么这一章是分水岭先看一个最朴素的C语言程序它从main函数的第一行开始一行一行往下执行直到return 0。这就是顺序结构。代码本身没有任何悬念计算机按顺序把每一行执行完程序就结束了。#include stdio.h int main(void) { int a 10; int b 20; int sum a b; printf(sum %d\n, sum); return 0; }但真实世界里几乎没有哪个需求是简单的“从上往下跑一遍”就能解决的。你做一个登录系统要先判断用户名和密码是否正确做一个计算器要判断用户按了哪个运算符统计一个班级的平均分要反复读入每一个学生的成绩。这些场景背后就是两件事根据条件做选择把同一件事重复做多次。前者叫分支后者叫循环。1.1 程序只会从上往下跑判断和重复怎么来这个道理听起来很简单但初学者的很多困惑恰恰是因为没把“程序从上往下跑”这句话放在心里。分支结构解决的是“走A还是走B”的问题循环结构解决的是“这条路能不能返回去再走一遍”的问题。打个比方你每天出门上学正常情况下是直线走到学校这就是顺序。但走到路口发现下雨了你决定回家拿伞这是分支。到了操场要跑5圈每跑完一圈就回到起点再跑一圈这是循环。程序里的逻辑本质上就是这三件事的排列组合。代码层面分支靠的是if、else、switch循环靠的是while、do-while、for。这一篇先把前面几个摸透。for循环涉及的内容比较多我打算放到下一篇“分支和循环下”里单独展开否则一下子塞太多容易消化不良。1.2 两个必须完成的思维转换从这一章开始你脑子里有一些习惯要主动扭转过来。第一变量不只是用来存数据的它还反过来决定程序的走向。之前你写int a 10只是让计算机记住这个数。现在你要习惯写if(a 0)让a的值去影响接下来的流程。变量从“被动存储”变成了“主动指挥”。第二重复的活可以交给机器前提是你要把“重复的规则”描述清楚。比如打印10行文字没有循环的时候只能复制粘贴10次printf有了循环一句printf加一个循环条件就够了。但代价是你得想清楚重复几次、从哪开始、什么时候停。这两个思维一旦建立你才算正式开始“写程序”而不是“抄代码”。2. if 和 else程序第一次有了“选择权”2.1 if的判定规则“非零即真”到底怎么理解if语句的基本形式很简单if (条件) { 语句; }条件成立就执行大括号里的内容条件不成立就跳过。所谓的“条件”在C语言里其实就是一个表达式。而对表达式结果的判定规则只有一条结果为0代表假结果非0代表真。很多初学者以为只有“成立”“不成立”这种说法其实C语言没有专门的布尔类型C89标准下它就是用整数来代表真假。关系运算符比如a b、score 60运算结果只有1或0。但如果你写if(-1)这个条件也是真的因为-1不是0。写if(a)只要a不等于0就执行。这带来一个实用建议判断条件尽量写清楚不要刻意玩“非零即真”的缩写。比如判断指针是否有效老手会写if(ptr)这是行业习惯但判断一个数是否是0你应该写if(a 0)而不是if(!a)虽然两者等价但后者读起来要绕一下。刚入门阶段把条件表达式写直白能少踩很多坑。一个典型例子判断奇偶数#include stdio.h int main(void) { int n; printf(请输入一个整数); scanf(%d, n); if (n % 2 0) { printf(偶数\n); } else { printf(奇数\n); } return 0; }判断奇偶数用的是n % 2 0%是取余运算n % 2的结果只可能是0或1等于0说明能被2整除就是偶数。这个例子也顺便告诉你if后面的括号里完全可以写一个由运算符组合而成的表达式而不是非要写一个单纯的变量。2.2 else和哪个if配对缩进说了不算if和else配对看起来简单实际有很多人在这里踩过坑。C语言的规定是else总是和它前面最近的一个尚未配对的if结合。缩进只是为了给人看编译器根本不看缩进。看这段代码if (a 0) if (b 0) printf(a和b都大于0\n); else printf(a不大于0\n);单看缩进else和第一个if对齐很多人以为它属于第一个if意思是“a不大于0时输出”。但实际上最近的未配对if是if(b 0)所以这个else属于第二个if。也就是说当a 0成立、b 0不成立时反而会打印“a不大于0”完全不是你的本意。修复办法就是加括号把逻辑隔离清楚if (a 0) { if (b 0) { printf(a和b都大于0\n); } } else { printf(a不大于0\n); }这就是为什么我会强烈建议只要if或else后面跟了语句不管是一条还是多条一律加大括号。这能避免两类问题一是这种else配对歧义二是以后往里加代码时不会忘记加括号而改变逻辑。2.3 用成绩等级判断把else if用熟当条件不止一个时可以连续写else if把多个分支串起来。最常见的案例就是成绩等级判断#include stdio.h int main(void) { int score; printf(请输入成绩); scanf(%d, score); if (score 90) { printf(优秀\n); } else if (score 80) { printf(良好\n); } else if (score 70) { printf(中等\n); } else if (score 60) { printf(及格\n); } else { printf(不及格\n); } return 0; }这段代码有一个非常重要的顺序逻辑判断顺序必须从高到低。如果先判断score 60那么85分就会直接进入“及格”分支后面的else if根本不会看这显然不对。所以区间型判断要注意每一层都覆盖了一部分可能值并且边界值要仔细测试。我建议你拿到这段代码后不要只输入一个分数就完事而是把90、80、70、60、59这几个值挨个试一遍。这个“拿边界值测试”的习惯从今天开始就要养成后面写循环、写数组时你会经常用到能省去大量排查bug的时间。3. switch 分支离散选项的快捷通道以及让人懵圈的case穿透3.1 switch的语法约束不是什么东西都能塞进caseswitch语句解决的是“同一个变量有几个不同取值每种取值走不同处理”的需求。它的语法是switch (表达式) { case 常量1: 语句; break; case 常量2: 语句; break; default: 语句; }C语言对case有几个硬性约束case后面的值必须是整型常量表达式可以是int、char、枚举但不能是浮点数、字符串也不能是变量。比如case 3.14、case a都会报错。switch的工作方式是先计算switch括号里表达式的值然后从上往下与各个case匹配找到匹配项后从那里开始执行。执行过程中如果遇到break直接跳出整个switch如果没遇到break就会继续执行后面的case。这就是下一小节要说的case穿透。另外同一个switch里case的值不能重复default可以放在任意位置但通常放在最后表示“以上都不匹配时走这里”。那么switch和if-else怎么选择我的经验是如果判断条件是大小比较、范围区间比如成绩区间用if-else如果是一个变量有好几个离散的固定取值比如菜单选项、状态码、字符按键用switch会让代码清晰很多。switch还有一个if没有的优势——可读性别人一眼就能看到你处理了哪几种情况。3.2 掉进case穿透坑里的完整现场下面这段代码很多人第一次运行都会惊到#include stdio.h int main(void) { int x 2; switch (x) { case 1: printf(数字1\n); case 2: printf(数字2\n); case 3: printf(数字3\n); default: printf(其他数字\n); } return 0; }运行结果是数字2、数字3、其他数字三行全部打印。因为x2匹配到了case 2执行printf(数字2\n)然后没有break程序继续往下走把case 3和default也都执行了。这个现象就叫case穿透。底层原因是switch本质上是根据case值生成的一个跳转表跳到对应入口之后如果没有break把流程截断代码就会一路跑到底。理解这一点之后你就能明白为什么“忘写break”是switch最常见的bug了。有时候我们也会故意利用穿透比如多个case要执行完全相同的代码switch (grade) { case A: case B: printf(成绩不错\n); break; case C: printf(还要努力\n); break; default: printf(输入不合法\n); }这里case A和case B共用同一个打印。注意case A后面没有break直接落到case B逻辑上就是A和B都执行同一段代码。这种利用是安全的但需要在代码里写清楚注释否则后来维护的人挺容易误会。3.3 用switch做菜单一个能立刻玩起来的例子switch最常见的应用场景就是菜单程序。用户输入一个数字程序根据数字执行不同功能直到用户选择退出。这里可以先用do-while做外层循环后面我们会详细讲do-while先感受一下组合的威力#include stdio.h int main(void) { int choice; do { printf( 学生成绩管理菜单 \n); printf(1. 录入成绩\n); printf(2. 查询成绩\n); printf(3. 统计平均分\n); printf(0. 退出\n); printf(请选择); scanf(%d, choice); switch (choice) { case 1: printf(你选择了录入成绩\n); break; case 2: printf(你选择了查询成绩\n); break; case 3: printf(你选择了统计平均分\n); break; case 0: printf(感谢使用再见\n); break; default: printf(无效选项请重新选择\n); } } while (choice ! 0); return 0; }这个程序里switch的分支很清楚default负责处理0以外的非法输入。do-while保证菜单至少显示一次并且只要用户没有选择0菜单就会反复出现。这就是实际项目里很常见的主菜单结构。你可以把菜单项替换成加减乘除计算器、通讯录增删查改的入口框架都能直接复用。4. while 与 do-while循环里的第一对兄弟差别不止一个分号4.1 while是“先看条件后干活”可能一次都不干循环是什么一句话只要条件为真循环体就一遍又一遍地执行。while的标准形态while (条件) { 语句; }执行过程是检查条件条件为真就执行循环体执行完再回来检查条件如果某一次检查时条件为假就跳到循环后面的代码。一个容易忽略的细节是如果一开始条件就是假的那么循环体一次都不会执行。用while求1到100的和#include stdio.h int main(void) { int sum 0; int i 1; while (i 100) { sum sum i; i i 1; } printf(1加到100的结果是%d\n, sum); return 0; }这里有三要素i 1是循环变量的初始值i 100是循环继续的条件i i 1是让条件最终变成假的更新。很多初学者写循环忘记最后一行结果i一直等于1i 100永远为真程序就死循环了CPU风扇直接起飞。所以写while时先问自己循环里的哪一行会让条件在未来的某一次检查中变成假你可能会问为什么sum sum i不能反过来写成i i 1在前其实先更新i再累加也可以只是求和结果就变成2到101了。顺序不影响语法但影响逻辑这正是我们需要用调试或printf观察中间值的原因。4.2 do-while是“先干一次再看条件”菜单输入很常用do-while的语法do { 语句; } while (条件);注意末尾有个分号这是初学者最容易漏掉的。执行过程是先执行一次循环体然后检查条件条件为真就再执行一次。所以do-while至少会执行一次循环体。当你的业务逻辑要求“无论条件是否满足至少先做一次”时用do-while最自然。举一个实际例子用户在按键输入y/n确认时你必须先让他输一次如果输了无效字符再提示重新输入。用do-while写char ch; do { printf(确定要退出吗(y/n)); scanf( %c, ch); } while (ch ! y ch ! Y ch ! n ch ! N);这里scanf的%c前面加了一个空格是为了把上一次输入后留在缓冲区里的换行符吃掉。这个小细节在写过几个输入程序后就会碰到先记下来。while和do-while的区别我用表格总结一下| 对比点 | while | do-while | | 判断时机 | 先判断后执行 | 先执行后判断 | | 最少执行次数 | 0次 | 1次 | | 循环体结束时 | 条件不满足跳出 | 判断条件再决定是否回去 | | 典型应用 | 处理未知长度的输入 | 菜单、输入校验 |这个表格看起来简单但真的决定了很多代码该用哪个循环。一个通用的判断法则是如果你不能确定条件第一次检查时是否成立用while如果业务逻辑上必须至少执行一次用do-while。4.3 差一错误和死循环两个必踩的坑与排查方法循环最容易出两类问题差一错误和死循环。先看差一错误。你想让某个动作恰好执行5次用i 5还是i 5如果初始i0i
返回列表