C 控制流
程序默认是从上到下顺序执行的,但真实世界的逻辑需要"做判断"和"重复执行"。控制流语句就是用来改变执行顺序的——条件分支(if/switch)让程序学会做选择,循环(for/while)让程序学会重复干活。本篇覆盖 C 全部控制流。
一、if / else 条件分支
if 是最基本的判断语句,根据条件真假选择执行哪段代码:
#include <stdio.h>
int main(void) {
int score = 85;
// 1. 基本 if
if (score >= 60) {
printf("及格了\n");
}
// 2. if-else
if (score >= 90) {
printf("优秀\n");
} else {
printf("继续努力\n");
}
// 3. if-else if-else(多分支)
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("F\n");
}
// 4. 单行可以省略花括号(但不推荐!)
if (score > 0)
printf("正数\n"); // 只有这一句属于 if
// 5. 嵌套 if
int age = 25;
if (age >= 18) {
if (age >= 65) {
printf("老年人\n");
} else {
printf("成年人\n");
}
}
return 0;
}几个要点:
- 条件表达式非 0 即真:负数、浮点数、指针都能当条件,只要非 0 就为真。
- 花括号建议永远写:即使只有一行也写,避免后续加代码时出错(苹果当年 goto fail 漏洞就是这么来的)。
- else if 不是独立关键字,本质是嵌套的 if-else。
二、switch 多分支
当需要根据一个变量的多个不同值做不同事时,用 switch 比 if-else if 链更清晰:
#include <stdio.h>
int main(void) {
int day = 3;
// switch:多分支选择,适合"等于某值"的场景
switch (day) {
case 1:
printf("星期一\n");
break; // break 不能漏!否则会"贯穿"
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
case 7: // 多个 case 共享代码
printf("周末\n");
break;
default: // 都不匹配时执行
printf("无效\n");
}
// 输出: 星期三
// 小心:漏 break 会"fall through"
int x = 1;
switch (x) {
case 1: printf("一\n"); // 没有break
case 2: printf("二\n"); // 会继续执行
case 3: printf("三\n"); // 也会执行
}
// 输出: 一 二 三(贯穿了)
return 0;
}switch 的三条铁律:
- case 后必须是常量(整数或字符),不能是变量或浮点。
- 每个 case 后要写 break!漏了 break 会"fall through"(贯穿)——继续执行下一个 case 的代码,直到遇到 break 或 switch 结束。
- default 处理意外情况,即使你觉得"不会发生"也加上,作为防御。
有时候"贯穿"是故意的——比如让多个 case 共享同一段代码(示例里 case 6 和 case 7 都打印"周末")。这时候加注释说明是有意为之。
三、for 循环
for 循环是 C 里用得最多的循环,语法是 for (初始化; 条件; 更新),三部分用分号分隔:
#include <stdio.h>
int main(void) {
// 1. 经典 for 循环
for (int i = 0; i < 5; i++) { // C99 起可以在 for 内声明 i
printf("%d ", i); // 0 1 2 3 4
}
printf("\n");
// 2. 倒序
for (int i = 5; i > 0; i--) {
printf("%d ", i); // 5 4 3 2 1
}
printf("\n");
// 3. 步长为 2
for (int i = 0; i <= 10; i += 2) {
printf("%d ", i); // 0 2 4 6 8 10
}
printf("\n");
// 4. 嵌套循环(经典:九九乘法表)
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= i; j++) {
printf("%d*%d=%-2d ", j, i, i*j);
}
printf("\n");
}
// 5. 省略某些部分
int i = 0;
for (; i < 3; ) { // 等价于 while (i < 3)
printf("%d ", i);
i++;
}
return 0;
}for 的执行顺序:初始化 → 条件判断 → 循环体 → 更新 → 条件判断 → 循环体 → 更新 → ...。三部分都可以省略(for (;;) 是死循环),但分号不能少。
四、while 与 do-while
while 循环适合"不确定循环多少次"的场景:
#include <stdio.h>
int main(void) {
// 1. while:先判断后执行(可能一次都不执行)
int i = 0;
while (i < 5) {
printf("%d ", i); // 0 1 2 3 4
i++;
}
printf("\n");
// while 最适合"不知道循环多少次"的场景
int n = 1024;
int count = 0;
while (n > 1) {
n /= 2;
count++;
}
printf("log2 = %d\n", count); // 10
// 2. do-while:先执行后判断(至少执行一次)
int num = 0;
do {
printf("num = %d\n", num);
num++;
} while (num < 0); // 条件为假,但循环体已执行了一次
// 经典用法:菜单循环(至少展示一次菜单)
int choice;
do {
printf("1. 开始 2. 设置 0. 退出\n");
printf("请选择:");
scanf("%d", &choice);
} while (choice != 0);
return 0;
}while 和 do-while 的区别:
- while:先判断条件,后执行循环体。可能一次都不执行。
- do-while:先执行循环体,后判断条件。至少执行一次。
经典场景:菜单循环(至少展示一次菜单)、输入验证(至少读一次输入)。
五、break 与 continue
这两个关键字用来改变循环的正常流程:
#include <stdio.h>
int main(void) {
// break:跳出当前循环
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // i=5 时整个循环结束
}
printf("%d ", i); // 0 1 2 3 4
}
printf("\n---\n");
// continue:跳过本次,进入下一次
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // 偶数跳过
}
printf("%d ", i); // 1 3 5 7 9
}
printf("\n---\n");
// break 只跳一层:嵌套循环时只跳出内层
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) break; // 只跳出内层 for
printf("%d-%d ", i, j);
}
}
// 输出: 0-0 1-0 2-0
printf("\n");
// 想一次跳出多层?用 goto(少有的合理用法)
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) goto done;
}
}
done:
printf("跳出多层循环\n");
return 0;
}- break:跳出当前这一层循环(或 switch),整个循环结束。
- continue:跳过本次循环剩下的部分,直接进入下一次循环。
- 嵌套循环时,break/continue 只对最内层循环生效,想一次跳出多层需要配合标志位或 goto。
六、goto(谨慎使用)
goto 可以无条件跳转到指定标签,几乎所有编程书都告诫"不要用 goto"——它会让代码变成"意大利面条",难以追踪。但 C 里有两个场景用 goto 反而更清晰:
#include <stdio.h>
int main(void) {
// goto:无条件跳转,几乎不推荐用!
// 唯一合理的两个场景:
// 场景1:跳出多层嵌套循环
int found = 0;
for (int i = 0; i < 10 && !found; i++) {
for (int j = 0; j < 10; j++) {
if (i * j == 42) {
printf("找到: %d*%d\n", i, j);
found = 1;
goto cleanup; // 跳出多层
}
}
}
cleanup:
printf("清理资源\n");
// 场景2:集中错误处理(类似 try-catch)
FILE *fp = NULL;
char *buf = NULL;
fp = fopen("x.txt", "r");
if (fp == NULL) goto error;
buf = malloc(100);
if (buf == NULL) goto error;
// 正常逻辑...
free(buf);
fclose(fp);
return 0;
error:
if (buf) free(buf);
if (fp) fclose(fp);
return 1;
}- 跳出多层嵌套循环:比用标志位 + 多层 if-break 简洁得多。
- 集中错误处理:Linux 内核风格,把 free/close 集中到一处,类似 try-catch。
除此之外坚决不要用 goto——尤其不要用 goto 往回跳(模拟循环),那是上世纪 70 年代的写法。
七、循环选择指南
- 已知循环次数:用
for。 - 未知次数,但先判断:用
while。 - 至少执行一次:用
do-while。 - 遍历数组:用
for,用下标访问。 - 读取输入直到 EOF:用
while (scanf(...) != EOF)。
小结
if-else 做条件判断,switch 做多值选择(记得 break),for/while/do-while 做循环,break 跳出、continue 跳过,goto 仅用于跳出多层或错误处理。下一篇我们看 C 的函数——把代码组织成可复用的单元。
← 上一篇 C 运算符
下一篇 C 函数 →