条件判断 — if/else 与 switch
C语言学习教程
条件判断 — if/else 与 switch
🌟 生活中的”条件判断”
想象你早上出门前看天气:
- 如果下雨 → 带伞
- 否则如果阴天 → 带折叠伞放包里
- 否则 → 不带伞,直接出门
这就是条件判断——根据不同情况做不同的事。程序也是一样,它不可能永远”一条路走到黑”。用户输入的密码对不对?分数及不及格?余额够不够付款?每一个”岔路口”都需要条件判断来决定程序走向。
一句话总结:条件判断让程序有了”选择”的能力。没有条件判断,程序只能机械地从上到下顺序执行,什么”智能”都谈不上。
📖 概念详解 — 程序从哪里开始分岔?
条件判断的本质
在 C 语言中,条件判断围绕一个核心规则展开:
0 表示”假”(false),非 0 表示”真”(true)
if (条件表达式) {
// 条件为真(非 0)时执行这里的代码
}
C 语言没有真正的 true/false 关键字(C99 引入了 stdbool.h,但底层仍然是 0 和 1)。理解这一点至关重要,因为很多”奇怪”的 bug 都和真假判断有关。
完整流程图
┌──────────────┐
│ 程序执行到 │
│ if 语句前 │
└──────┬───────┘
│
▼
┌──────────────┐
│ 计算条件 │
│ 表达式的值 │
└──────┬───────┘
│
┌────┴────┐
│ 是真的? │
└────┬────┘
是 ╱ ╲ 否
▼ ▼
┌──────────┐ ┌──────────┐
│ 执行 if │ │ 跳过 if │
│ 块内代码 │ │ 块内代码 │
└────┬─────┘ └────┬─────┘
│ │
▼ ▼
┌──────────────────────┐
│ 继续执行 if 之后 │
│ 的代码(汇合点) │
└──────────────────────┘
if-else if-else 流程
┌──────────┐
│ 条件 1 │
└────┬─────┘
真 ╱ ╲ 假
▼ ▼
┌──────┐ ┌──────────┐
│代码块1│ │ 条件 2 │
└──┬───┘ └────┬─────┘
│ 真 ╱ ╲ 假
│ ▼ ▼
│ ┌──────┐ ┌──────────┐
│ │代码块2│ │ 条件 3 │
│ └──┬───┘ └────┬─────┘
│ │ 真 ╱ ╲ 假
│ │ ▼ ▼
│ │ ┌──────┐ ┌──────┐
│ │ │代码块3│ │ else │
│ │ └──┬───┘ │代码块 │
│ │ │ └───┬───┘
▼ ▼ ▼ ▼
┌────────────────────────────────────┐
│ 所有分支最终都到这里(汇合点) │
└────────────────────────────────────┘
关键点:从上到下检查,第一个为真的条件执行后,剩余分支全部跳过。 这是一个”胜利者通吃”的机制。
💻 代码示例 — 从简单到复杂
示例 1:最简单的 if(单分支)
#include <stdio.h>
int main() {
int score = 85;
// 只有一个分支:及格了才打印
if (score >= 60) {
printf("恭喜,你及格了!\n");
}
// 这行无论及不及格都会执行
printf("程序结束。\n");
return 0;
}
运行结果:
恭喜,你及格了!
程序结束。
如果 score = 55:
程序结束。
花括号 {} 只在代码块超过一行时必须写。但强烈建议始终写花括号,这是无数 bug 换来的教训。
示例 2:if-else(双分支)
#include <stdio.h>
int main() {
int temperature = 38;
if (temperature > 37) {
printf("你发烧了,体温 %.1f°C\n", (float)temperature);
printf("建议去看医生。\n");
} else {
printf("体温正常:%d°C\n", temperature);
printf("保持健康!\n");
}
return 0;
}
if-else 是互斥的:要么执行 if,要么执行 else,不可能两者都执行。
示例 3:if-else if-else(多分支 — 成绩等级)
#include <stdio.h>
int main() {
int score;
printf("请输入成绩 (0-100): ");
scanf("%d", &score);
// 注意条件顺序:从高到低写
if (score >= 90 && score <= 100) {
printf("等级:A(优秀)\n");
printf("评语:继续保持,你很棒!\n");
} else if (score >= 80) {
// 能到这里说明 score < 90,所以只需判断 >= 80
printf("等级:B(良好)\n");
printf("评语:不错,还有进步空间。\n");
} else if (score >= 70) {
printf("等级:C(中等)\n");
printf("评语:基础掌握了,继续加油。\n");
} else if (score >= 60) {
printf("等级:D(及格)\n");
printf("评语:勉强过关,需要多练习。\n");
} else if (score >= 0) {
printf("等级:F(不及格)\n");
printf("评语:不要灰心,找到薄弱点再攻破。\n");
} else {
// 小于 0 的无效输入
printf("输入错误!成绩不能为负数。\n");
}
return 0;
}
重点讲解:
- 条件从上到下”逐级过滤”。到了
else if (score >= 80)时,已经确定score < 90,所以不需要写成score >= 80 && score < 90。 - 条件顺序很重要——如果先写
>= 60,后面的条件永远不会匹配! else是”兜底”分支,所有不满足前面条件的情况都会掉到这里。
示例 4:嵌套 if(多条件组合 — 网吧入场判断)
#include <stdio.h>
int main() {
int age;
int hasIdCard; // 1 = 有身份证, 0 = 没有
int isVip; // 1 = 会员, 0 = 非会员
printf("请输入年龄: ");
scanf("%d", &age);
printf("有身份证吗?(1=有, 0=没有): ");
scanf("%d", &hasIdCard);
printf("是会员吗?(1=是, 0=不是): ");
scanf("%d", &isVip);
if (age >= 18) {
// 成年人:检查身份证
if (hasIdCard) {
printf("✅ 允许进入。\n");
// 进一步检查是否会员
if (isVip) {
printf(" 尊贵会员,请上二楼包厢。\n");
} else {
printf(" 普通用户,请在一楼大厅。\n");
}
} else {
printf("❌ 请出示身份证。\n");
}
} else {
// 未成年人
printf("❌ 未满18岁,禁止进入网吧。\n");
printf(" 请在监护人陪同下使用电脑。\n");
}
return 0;
}
嵌套 if 要点:
- 外层条件先判断,内层是对外层的”细化”。
- 嵌套不要太深(超过 3 层说明可能需要重新设计逻辑)。
- 缩进是关键——混乱的缩进 = 混乱的逻辑。
示例 5:三元运算符(一行写完的 if-else)
#include <stdio.h>
int main() {
int a = 10, b = 20;
// 常规写法
int max;
if (a > b) {
max = a;
} else {
max = b;
}
printf("(常规) 较大值是: %d\n", max);
// 三元运算符写法:条件 ? 真时的值 : 假时的值
int max2 = (a > b) ? a : b;
printf("(三元) 较大值是: %d\n", max2);
// 三元运算符也可以嵌套(但不建议太复杂)
int c = 30;
int max3 = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
printf("三者中最大是: %d\n", max3);
// 实战用法:奇偶判断
int num = 7;
printf("%d 是%s\n", num, (num % 2 == 0) ? "偶数" : "奇数");
return 0;
}
使用建议:三元运算符适合简单的”二选一赋值”,不要把大段逻辑塞进去,否则代码将非常难读。
示例 6:switch-case(多值精确匹配 — 简易计算器)
#include <stdio.h>
int main() {
char operator;
double num1, num2, result;
printf("简易计算器\n");
printf("请输入算式 (如: 5 + 3): ");
scanf("%lf %c %lf", &num1, &operator, &num2);
switch (operator) {
case '+':
result = num1 + num2;
printf("%.2f + %.2f = %.2f\n", num1, num2, result);
break;
case '-':
result = num1 - num2;
printf("%.2f - %.2f = %.2f\n", num1, num2, result);
break;
case '*':
case 'x':
case 'X':
// 三种输入方式都当作乘法(* 或 x 或 X)
result = num1 * num2;
printf("%.2f × %.2f = %.2f\n", num1, num2, result);
break;
case '/':
if (num2 != 0) {
result = num1 / num2;
printf("%.2f / %.2f = %.2f\n", num1, num2, result);
} else {
printf("错误:除数不能为 0!\n");
}
break;
default:
printf("不支持的运算符:%c\n", operator);
printf("支持的运算符:+ - * /\n");
}
return 0;
}
switch 要点:
switch(表达式)中的表达式必须是整数类型(int、char、enum 等,不能是 float 或字符串)。case后面必须是常量,不能是变量或范围。- 每个
case结尾必须有break,否则会”穿透”到下一个 case。 default是可选的,但建议写上以处理意外情况。
多个 case 合并:case '*': case 'x': case 'X': 三个标签共享同一段代码,一个 break 统一跳出。
⚠️ 常见错误 — 这些都是血泪教训
错误 1:if 后面多了分号(空语句陷阱)
| 错误写法 | 正确写法 |
|---|---|
if (x > 0); ← 分号结束了 if! | if (x > 0) |
printf("正数\n"); ← 永远执行 | printf("正数\n"); |
// ❌ 错误演示
int x = -5;
if (x > 0); // 分号 = 空语句,if 什么都不做
printf("x 是正数\n"); // 这行跟 if 没关系,永远执行!
// 输出:x 是正数(明明 x = -5!)
// ✅ 正确写法
int x = -5;
if (x > 0) // 没有分号
printf("x 是正数\n"); // 只有 x > 0 时才执行
// 输出:(无输出,正确!)
原理:在 C 语言中,单独一个分号 ; 是一条合法语句(空语句,什么也不做)。if (x > 0); 的意思是”如果 x > 0,执行空语句”,然后 printf(...) 跟 if 毫无关系。这可能是 C 语言中最隐蔽的 bug 之一。
错误 2:赋值 = 和比较 == 混淆
| 错误写法 | 正确写法 |
|---|---|
if (x = 5) ← 赋值,不是比较 | if (x == 5) ← 这才是比较 |
// ❌ 错误演示
int x = 3;
if (x = 5) { // 把 5 赋给 x,然后判断 x(5 非 0 = 真)
printf("相等\n"); // 永远会打印!
}
printf("x = %d\n", x); // x 变成了 5!
// 输出:相等
// x = 5
// ✅ 正确写法
int x = 3;
if (x == 5) { // 判断 x 是否等于 5
printf("相等\n"); // 不执行,因为 3 != 5
}
printf("x = %d\n", x); // x 还是 3
// 输出:x = 3
防御技巧:将常量写在左边 — if (5 == x)。如果误写成 if (5 = x),编译器会报错(因为不能给常量赋值),帮你提前发现 bug。
错误 3:字符串不能用 == 比较
| 错误写法 | 正确写法 |
|---|---|
if (name == "Tom") | if (strcmp(name, "Tom") == 0) |
#include <stdio.h>
#include <string.h>
int main() {
char name[20];
printf("请输入名字: ");
scanf("%s", name);
// ❌ 错误:比较的是内存地址,不是内容
if (name == "Tom") {
printf("错误的比较方式\n");
}
// ✅ 正确:使用 strcmp 函数
if (strcmp(name, "Tom") == 0) {
printf("Hello, Tom!\n");
} else {
printf("你不是 Tom\n");
}
return 0;
}
原理:C 语言中,数组名(如 name)代表数组首地址。name == "Tom" 比较的是两个地址是否相同,而不是字符串内容是否相等。必须用 <string.h> 中的 strcmp() 函数。
错误 4:switch 忘记 break(穿透 bug)
// ❌ 错误演示
int day = 1;
switch (day) {
case 1:
printf("Monday\n"); // 没有 break!
case 2:
printf("Tuesday\n"); // 也会执行!
case 3:
printf("Wednesday\n"); // 也会执行!
}
// 输出:Monday
// Tuesday
// Wednesday
// 竟然输出了三行!
// ✅ 正确写法
int day = 1;
switch (day) {
case 1:
printf("Monday\n");
break; // 必须有 break
case 2:
printf("Tuesday\n");
break;
case 3:
printf("Wednesday\n");
break;
}
// 输出:Monday (仅一行)
什么时候可以不写 break? 当你确实需要多个 case 共享同一段代码时(比如上面的计算器中 case '*': case 'x': case 'X':)。
错误 5:复合条件没有用括号
// ❌ 不好读,容易出错
if (age >= 18 && hasIdCard || isVip) // 运算符优先级是什么?
printf("允许进入\n");
// ✅ 用括号明确逻辑
if ((age >= 18 && hasIdCard) || isVip)
printf("允许进入\n");
优先级:&& 优先于 ||,但不要依赖记忆——加括号,让意图一目了然。
💡 练习建议
- 从纸上开始:在写代码前,用笔画出流程图。搞清楚”先判断什么、再判断什么”,代码自然水到渠成。
- 测试边界值:写完成绩判断程序后,分别输入 -1、0、59、60、89、90、100、101 测试,确保每个边界都正确。
- 故意犯错:故意在
if后加分号,或忘记break,或把==写成=,看看程序会怎么”歪”。理解 bug 比写出正确代码更有价值。 - 改写练习:把一个 if-else 改成 switch(如果可以的话),再改回来。体会两种结构各自适合什么场景。
- 多条件拆解:复杂条件不要写一行。拆成多个 if,用变量存中间结果,代码可读性会大幅提升。
📝 要点速查
| 要点 | 说明 |
|---|---|
if (条件) { } | 条件为真(非 0)时执行,为假(0)时跳过 |
else | 与最近的 if 配对,条件不成立时执行 |
else if | 链式多分支,从上到下匹配,命中后跳过后续 |
switch(v) | 适合判断一个整数变量的多个具体值 |
break | 在 switch 中阻止”穿透” |
default | switch 的”兜底”分支(可选但建议写) |
三元 ? : | 简单二选一的简写,不要嵌套过深 |
= vs == | 赋值 vs 比较,C 语言头号陷阱 |
strcmp() | 字符串比较必须用函数,不能用 == |
🔗 相关主题
- 循环 — for/while 重复执行 — 条件判断经常和循环搭配使用(用 if 决定何时 break)
- 函数 — 代码复用的基本单元 — 把复杂的条件判断封装成函数
- 运算符与表达式 — 关系运算符(
> < >= <= == !=)和逻辑运算符(&& || !) - 练习 — 控制流综合题 — 用条件判断解决实际问题的 10 道练习