下面我把C语言分支结构的所有知识点从头到尾过一遍,配上几个经典例子,看完你要是还迷糊,那就再看一遍。
C语言在线编程环境:https://sky315.com:8080/c/。
一、分支结构就两种,搞明白区别就赢了八成
C语言的分支结构其实就两大类:if语句和switch语句。它们都能实现“根据条件走不同的路”,但适用的场景不一样。
if语句——条件判断的万能选手
if语句有四种形态,从简单到复杂:
-
单分支:
if (条件) { 要做的事 }——条件成立就干,不成立就拉倒。 -
双分支:
if (条件) { 真的时候干这个 } else { 假的时候干那个 }——两条路,必选一条。 -
多分支:
if (条件1) { ... } else if (条件2) { ... } else if (条件3) { ... } else { ... }——多个条件挨个试,谁先成立就进谁。 -
嵌套if:if里面再套if,层层递进。
if语句的执行逻辑就一句话:条件为真(非0)就进,条件为假(0)就跳过。C语言里没有专门的布尔类型(C99才有_Bool),条件判断全靠数值——0就是假,非0就是真。这点跟其他语言不一样,得记住。
switch语句——多选一的精准武器
switch (表达式) { case 常量1: 语句块1; break; case 常量2: 语句块2; break; default: 默认语句块; break; }
switch适合一个表达式的值等于哪个常量就执行哪个分支。它的特点是:表达式的值必须是整型或字符型,不能是浮点数,也不能是字符串。每个case后面跟的是常量,不能是变量。
switch有个最要命的特性:穿透。 如果一个case后面没有break,执行完这个case会继续往下执行下一个case,直到遇到break或者switch结束。这个特性有时候是坑,有时候反倒是你要的效果。
二、if-else配对问题,这个坑不知道要吃大亏
先看一段代码,你猜else跟哪个if配对?
if (a > 0) if (b > 0) printf("a和b都大于0\n"); else printf("a不大于0\n");
很多人看缩进以为else跟第一个if配对,但C语言不看缩进,它看的是语法规则。规则是:else总是和它上面最近的那个未配对的if配对。所以这里的else实际上是跟if (b > 0)配对,不是跟if (a > 0)配对。
想让它跟第一个if配对?加花括号:
if (a > 0) { if (b > 0) printf("a和b都大于0\n"); } else { printf("a不大于0\n"); }
花括号把内层if框起来了,else就够不着它了,只能老老实实跟外层if配对。写if语句的时候,该加花括号就加,别偷懒,否则这种配对问题会让你调Bug调到怀疑人生。
三、典型例题挨个讲,每个都比光看语法管用
下面上几个经典例子,你挨个看懂,分支结构的基本功就差不多了。
例1:成绩等级划分——最基本的if-else if多分支
输入一个百分制成绩,输出对应的等级:90以上是A,80-89是B,70-79是C,60-69是D,60以下是E。
int score; scanf("%d", &score); if (score >= 90) { printf("A\n"); } else if (score >= 80) { printf("B\n"); } else if (score >= 70) { printf("C\n"); } else if (score >= 60) { printf("D\n"); } else { printf("E\n"); }
关键是条件的顺序。你可能觉得写成if (score >= 60 && score < 70)更严谨,其实没必要——因为if-else if是从上到下匹配,一旦匹配成功,后面的else if就不再判断了。score=85的时候,第一个条件90不满足,第二个80满足,进去之后后面的70、60根本不会执行。
如果你把顺序反过来,先判断if (score >= 60),那85分就会被打成D,因为60这个条件先成立了。条件顺序决定了结果,写的时候脑子里得过一遍执行流程。
例2:判断闰年——条件组合怎么玩
闰年的规则:能被4整除但不能被100整除,或者能被400整除。
int year; scanf("%d", &year); if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { printf("%d是闰年\n", year); } else { printf("%d不是闰年\n", year); }
这题考的是逻辑运算符的优先级和组合。&&优先级比||高,所以上面那个条件不加括号其实也能正确解析,但加了括号让代码更清晰。写分支条件的时候,括号不要舍不得用,多写几个括号让优先级一目了然,比让读你代码的人猜来猜去强多了。
例3:分段函数——多个if的写法区别
数学上分段函数很常见,比如:
-
x < 0 时,y = -1
-
x = 0 时,y = 0
-
x > 0 时,y = 1
这个用if-else if写:
if (x < 0) { y = -1; } else if (x == 0) { y = 0; } else { y = 1; }
注意这里不能写成三个独立的if:
// 错误写法 if (x < 0) y = -1; if (x == 0) y = 0; if (x > 0) y = 1;
三个独立if的话,每个条件都会判断一遍,虽然结果没错(因为三个条件互斥,不会同时成立),但效率不如if-else if。更关键的是,如果条件不互斥,三个独立if可能会执行多个分支,而if-else if只会执行第一个匹配的。 这两种写法有本质区别。
例4:简单计算器——switch的经典应用
输入两个数和运算符(+、-、*、/),输出计算结果。
float a, b; char op; scanf("%f %c %f", &a, &op, &b); switch (op) { case '+': printf("%.2f\n", a + b); break; case '-': printf("%.2f\n", a - b); break; case '*': printf("%.2f\n", a * b); break; case '/': if (b != 0) { printf("%.2f\n", a / b); } else { printf("除数不能为0\n"); } break; default: printf("不支持的运算符\n"); break; }
这个例子把switch用得很标准。每个case后面都有break,防止穿透。default处理了“用户瞎输入”的情况。除法这里还嵌套了一个if判断,处理除零的异常——分支结构嵌套是家常便饭,不要觉得嵌套就复杂了,一层一层分析就行。
顺便说一句,不加break的switch有时候是有用的,比如你想让多个case执行同样的操作:
switch (grade) { case 'A': case 'B': printf("优秀或良好\n"); break; case 'C': printf("及格\n"); break; default: printf("不及格\n"); break; }
输入'A'或者'B'都会打印同样的内容,这就是利用穿透达到“或”的效果。但这种写法新手容易懵,等熟练了再用,不熟练就老老实实每个case写break。
例5:三个数排序——if嵌套帮你理清逻辑
输入三个整数,按从小到大的顺序输出。
int a, b, c, temp; scanf("%d%d%d", &a, &b, &c); if (a > b) { temp = a; a = b; b = temp; } if (a > c) { temp = a; a = c; c = temp; } if (b > c) { temp = b; b = c; c = temp; } printf("%d %d %d\n", a, b, c);
这个写法很巧妙。第一个if保证a <= b,第二个if保证a <= c,所以a就成了最小值。前两个if执行完,a确定是最小的,剩下b和c谁大谁小再比一下。三个if依次执行,逻辑层层推进。这种题看着简单,但能帮你理解分支是怎么通过顺序执行解决问题的。
例6:判断三角形类型——条件的排列组合
输入三条边长,判断能不能构成三角形,如果能,再判断是等边、等腰还是一般三角形。
int a, b, c; scanf("%d%d%d", &a, &b, &c); // 先判断能不能构成三角形 if (a + b > c && a + c > b && b + c > a) { // 能构成三角形,再判断类型 if (a == b && b == c) { printf("等边三角形\n"); } else if (a == b || a == c || b == c) { printf("等腰三角形\n"); } else { printf("一般三角形\n"); } } else { printf("不能构成三角形\n"); }
外层判断能不能构成,内层判断是什么类型,这就是分支嵌套的典型场景。逻辑层次要清晰,先大后小,先判断必要条件,再判断充分条件。
四、几个常见坑,提前踩过就长记性了
坑1:赋值号当等号用
if (a = 5) { printf("a等于5\n"); }
这个语法是对的,不会报错,但意思全变了。a = 5是赋值,把5赋给a,整个表达式的值就是5,5是非0为真,所以这个if永远成立。你本来想判断a == 5,结果写成赋值,Bug就这么埋下了。很多老手都栽过这个跟头,有个小技巧:把常量写在左边,写成if (5 == a),万一你少写一个等号变成if (5 = a),编译器直接报错,因为常量不能赋值。
坑2:else悬空问题,前面讲过了,不啰嗦。
坑3:switch里漏了break
这个太常见了,你写着写着就忘了,结果程序跑出来结果莫名其妙。每次写完switch,从头检查一遍是不是每个case后面都有break(除非你故意要穿透)。
坑4:浮点数直接比较相等
float x = 0.1 + 0.2; if (x == 0.3) { // 你以为会进来,其实大概率进不来 }
浮点数存储有误差,0.1+0.2不一定等于0.3。分支条件里尽量避免用浮点数的==或!=比较,改成判断差值是否小于一个很小的数,比如if (fabs(x - 0.3) < 1e-6)。
坑5:if-else if和多个独立if的混淆
这个前面讲过了,记清楚:多个独立if可能执行多个分支,if-else if最多只执行一个分支。
五、总结几句
分支结构说到底就是让程序有了“判断能力”,能根据不同情况做不同的事。if语句灵活万能,适合范围判断、复杂条件;switch语句干脆利落,适合单值匹配。
练到什么程度算过关? 拿到一个需求,你能判断出来该用if还是switch,能写对条件表达式,能理清嵌套层次,能把边界条件想全了——这就算分支结构真正掌握了。
还是那句话,光看没用,打开编译器自己敲。每个例子自己写一遍,改改条件换换数据跑一跑,出几次错修几次,这些东西就长在你身上了。