C语言:语句——选择结构与循环结构
一、选择结构
选择结构的核心是“根据条件真假执行不同代码”,C语言提供if系列和switch-case两种实现方式,适用于不同场景。
1.1 if系列语句:灵活的条件判断
if系列语句通过布尔表达式(真/假)控制代码执行,支持单分支、双分支、多分支和嵌套,灵活性极高。
(1)核心语法与逻辑拆解
| 语句类型 | 语法格式 | 核心逻辑 |
|---|---|---|
| 单分支if | if (条件) { 代码块; } | 条件为真则执行代码块,假则跳过 |
| 双分支if-else | if (条件) { 代码块1; } else { 代码块2; } | 条件为真执行代码块1,假则执行代码块2(二选一) |
| 多分支if-else if | if (条件1) { ... } else if (条件2) { ... } else { ... } | 依次判断条件,满足第一个条件即执行对应代码块,后续条件不再判断(互斥) |
| 嵌套if | if (外层条件) { if (内层条件) { ... } else { ... } } | 外层条件满足后,再判断内层条件(适合多维度判断) |
(2)实例
① 单分支与双分支(基础场景)
int score;
printf("请输入成绩: ");
scanf("%d", &score);
// 单分支if:仅判断“及格”情况
if (score >= 60 && score <= 100) {
printf("恭喜,及格了!\n"); }
// 双分支if-else:及格/不及格二选一
if (score >= 90 && score <= 100) {
printf("优秀!\n");
} else {
printf("继续努力!\n"); }
关键细节:
- 条件表达式必须是“布尔值”(C语言中0为假,非0为真);
- 若代码块只有1行,
{}可省略,但建议始终加上(避免后续扩展代码出错); - 多条件判断需用
&&(逻辑与)、||(逻辑或)连接,注意优先级(&&高于||)。
② 多分支if-else if(范围判断)
适合“连续区间判断”(如成绩等级、年龄分段):
#include <stdio.h>
int main() {
int score;
printf("请输入成绩(0-100): ");
scanf("%d", &score);
// 多分支:互斥条件,从高到低判断
if (score >= 90 && score <= 100) {
printf("成绩等级: A(优秀)\n");
} else if (score >= 80) { // 隐含条件:score < 90
printf("成绩等级: B(良好)\n");
} else if (score >= 70) { // 隐含条件:score < 80
printf("成绩等级: C(中等)\n");
} else if (score >= 60) { // 隐含条件:score < 70
printf("成绩等级: D(及格)\n");
} else if (score >= 0) { // 处理0-59的无效成绩
printf("成绩等级: E(不及格)\n");
} else {
printf("输入无效!成绩应在0-100之间\n");
}
return 0;
}
逻辑优化:
多分支判断时,建议按“条件范围从大到小/从小到大”排序,避免条件重叠;最后加else处理所有异常情况(如输入负数),增强程序健壮性。
③ 嵌套if(多维度判断)
适合“先满足外层条件,再判断内层条件”(如“成年+性别”判断):
int main() {
int age;
char gender;
printf("请输入年龄: ");
scanf("%d", &age);
printf("请输入性别(M/F): ");
scanf(" %c", &gender); // 空格用于吸收前一个输入的回车符
// 外层判断“是否成年”,内层判断“性别”
if (age >= 18) {
if (gender == 'M' || gender == 'm') {
printf("成年男性\n");
} else if (gender == 'F' || gender == 'f') {
printf("成年女性\n");
} else {
printf("性别输入错误\n");
}
} else {
printf("未成年人\n");
}
1.2 switch-case语句:高效的多值匹配
switch-case适用于“变量与多个固定值匹配”的场景(如菜单选择、月份判断),执行效率高于多分支if-else if,语法更简洁。
(1)核心语法与逻辑原理
switch (变量) {
case 值1: 代码块1; break;
case 值2: 代码块2; break;
...
default: 默认代码块; break;
}
逻辑拆解:
- 变量与
case后的值进行“全等匹配”(仅支持整数/字符/枚举类型,不支持浮点数、字符串); - 匹配成功后,从该
case开始顺序执行,直到遇到break或switch结束; - 若所有
case都不匹配,执行default(可选,但建议加上,处理异常情况)。
(2)关键特性:穿透性(灵活运用+避坑)
switch-case的“穿透性”是核心特性——若case后没有break,会继续执行下一个case的代码
① 穿透性
例如“按月份判断季节”:12、1、2月都是冬季,无需重复写代码:
int month;
printf("请输入月份(1-12): ");
scanf("%d", &month);
switch (month) {
case 12:
case 1: // 穿透:case12匹配后,会执行case1、case2的代码
case 2:
printf("%d月是冬季\n", month);
break; // 终止穿透
case 3:
case 4:
case 5:
printf("%d月是春季\n", month);
break;
case 6:
case 7:
case 8:
printf("%d月是夏季\n", month);
break;
case 9:
case 10:
case 11:
printf("%d月是秋季\n", month);
break;
default:
printf("无效的月份!\n");
break;
}
② 避免无意穿透
若忘记写break,会导致“多case执行”的bug:
// 错误示例:case1后无break
switch (choice) {
case 1:
printf("查询余额\n"); // 执行后会继续执行case2的代码
case 2:
printf("存款\n");
break;
}
// 若choice=1,会输出:查询余额 存款(不符合预期)
(3)字符类型的switch-case(大小写兼容)
通过case 'A': case 'a':的方式,可兼容大小写输入,无需额外判断:
char grade;
printf("请输入成绩等级(A-E): ");
scanf("%c", &grade);
switch (grade) {
case 'A':
case 'a':
printf("优秀!\n");
break;
case 'B':
case 'b':
printf("良好!\n");
break;
case 'C':
case 'c':
printf("中等!\n");
break;
case 'D':
case 'd':
printf("及格!\n");
break;
case 'E':
case 'e':
printf("不及格!\n");
break;
default:
printf("无效的等级!\n");
break;
}
1.3 if vs switch-case
| 对比维度 | if系列语句 | switch-case语句 |
|---|---|---|
| 匹配类型 | 支持范围判断(如score >= 60)、复杂条件 | 仅支持固定值匹配(如choice=1) |
| 灵活性 | 高(可嵌套、多条件组合) | 低(仅固定值匹配) |
| 执行效率 | 多分支时较低(依次判断) | 较高(直接匹配case,无顺序依赖) |
| 代码简洁性 | 多分支时冗长 | 多值匹配时简洁(避免重复条件判断) |
| 适用场景 | 成绩等级、年龄分段等范围判断 | 菜单选择、月份、运算符等固定值匹配 |
二、循环结构
循环结构的核心是“满足条件时重复执行代码块”,C提供for、while、do-while三种循环,以及break、continue、goto三种循环控制语句。
2.1 三种循环的核心差异与用法
三种循环的本质都是“条件判断+重复执行”,但语法结构和适用场景不同,核心差异在于“初始化、条件判断、增量更新”的位置。
(1)for
for循环将“初始化、条件判断、增量更新”集中在一行,结构清晰,适合“已知循环次数”的场景(如遍历数组、固定次数迭代)。
核心语法:
for (初始化表达式; 条件表达式; 增量表达式) {
循环体;
}
执行流程:
- 执行1次初始化表达式(如
int i=0); - 判断条件表达式:为真则执行循环体,为假则退出循环;
- 执行增量表达式(如
i++); - 回到步骤2,重复判断。
灵活用法拆解:
① 基础循环(递增、递减、步长非1):
#include <stdio.h>
int main() {
// 1. 递增循环(1-10)
printf("1-10的数字: ");
for (int i = 1; i <= 10; i++) {
printf("%d ", i);
}
printf("\n");
// 2. 递减循环(10-1)
printf("10-1的数字: ");
for (int i = 10; i >= 1; i--) {
printf("%d ", i);
}
printf("\n");
// 3. 步长非1(1-10的奇数,步长=2)
printf("1-10的奇数: ");
for (int i = 1; i <= 10; i += 2) {
printf("%d ", i);
}
printf("\n");
return 0;
}
② 多循环变量(同步控制两个变量,利用逗号表达式):
// 同时递增i、递减j
for (int i = 0, j = 10; i < j; i++, j--) {
printf("(%d, %d) ", i, j); // 输出:(0,10) (1,9) (2,8) (3,7) (4,6)
}
③ 省略表达式(灵活适配不同场景):
#include <stdio.h>
int main() {
// 1. 完全省略(无限循环,需用break退出)
printf("1. 完全省略表达式: ");
int count = 0;
for (;;) { // 初始化、条件、增量都省略
printf("%d ", count++);
if (count >= 5) break; // 必须手动退出
}
printf("\n");
// 2. 省略初始化(初始化在循环外)
printf("2. 省略初始化表达式: ");
int i = 0;
for (; i < 3; i++) { // 省略初始化
printf("i=%d ", i);
}
printf("\n");
// 3. 省略条件表达式(无限循环,靠break退出)
printf("3. 省略条件表达式: ");
for (int j = 0; ; j++) {
printf("j=%d ", j);
if (j >= 2) break;
}
printf("\n");
// 4. 省略增量表达式(增量在循环体内)
printf("4. 省略增量表达式: ");
for (int k = 0; k < 3; ) { // 省略增量
printf("k=%d ", k);
k++; // 增量在循环体内
}
printf("\n");
return 0;
}
注意:省略表达式时,;不能省略
(2)while
while循环将“条件判断”放在循环开头,语法简洁,适合“未知循环次数,仅知道终止条件”的场景(如用户输入控制、文件读取)。
核心语法:
while (条件表达式) {
循环体;
}
执行流程:先判断条件,为真则执行循环体,否则直接退出(可能一次都不执行)。
① 基本循环(1-5):
#include <stdio.h>
int main() {
int i = 1;
printf("1-5的数字: ");
while (i <= 5) {
printf("%d ", i);
i++; // 增量更新必须写在循环体内,否则会无限循环
}
printf("\n");
return 0;
}
② 用户输入控制循环(输入0结束):
#include <stdio.h>
int main() {
int number, sum = 0;
printf("请输入数字(输入0结束): ");
scanf("%d", &number);
// 条件:number != 0(输入0时退出)
while (number != 0) {
sum += number;
printf("当前总和: %d\n", sum);
printf("请输入数字(输入0结束): ");
scanf("%d", &number);
}
printf("最终总和: %d\n", sum);
return 0;
}
避坑点:循环体内必须有“改变条件的语句”(如number = 0或i++),否则会陷入无限循环。
(3)do-while 至少执行一次
do-while循环将“条件判断”放在循环末尾,无论条件是否为真,循环体至少执行一次,适合“必须先执行一次”的场景(如菜单交互、密码输入验证)。
核心语法:
do {
循环体;
} while (条件表达式);
执行流程:先执行循环体,再判断条件,为真则继续循环,否则退出。
菜单交互(必须显示一次菜单)
#include <stdio.h>
int main() {
int choice;
// 先显示菜单,再判断是否退出
do {
printf("\n=== 菜单 ===\n");
printf("1. 开始游戏\n");
printf("2. 设置\n");
printf("3. 退出\n");
printf("请选择: ");
scanf("%d", &choice);
switch (choice) {
case 1: printf("游戏开始!\n"); break;
case 2: printf("进入设置\n"); break;
case 3: printf("退出程序\n"); break;
default: printf("无效选择,请重新输入!\n"); break;
}
} while (choice != 3); // 选择3时退出
return 0;
}
关键差异:若用while实现,首次菜单需要手动写一次,而do-while直接简化代码。
2.2 循环控制语句:break、continue、goto
循环控制语句用于改变循环的默认执行流程,灵活运用可让循环更高效。
(1)break:终止当前循环
- 作用:立即退出当前层循环(嵌套循环中不影响外层);
- 适用场景:找到目标值后退出、满足异常条件时退出。
#include <stdio.h>
int main() {
// 示例1:找到第一个能被7整除的数
printf("找到第一个能被7整除的数: ");
for (int i = 1; i <= 100; i++) {
if (i % 7 == 0) {
printf("%d\n", i); // 输出:7
break; // 找到后立即退出循环
}
}
// 示例2:嵌套循环中,break只退出内层
printf("\n嵌套循环中的break:\n");
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (i * j > 4) {
printf("i=%d, j=%d时break(退出内层)\n", i, j);
break; // 仅退出内层循环,外层继续执行
}
printf("i=%d, j=%d\n", i, j);
}
}
return 0;
}
(2)continue:跳过本次循环
- 作用:跳过循环体中
continue后的代码,直接进入下一次循环(不终止循环); - 适用场景:跳过特定条件的处理(如跳过奇数、跳过零值)。
#include <stdio.h>
int main() {
// 示例1:输出1-10的偶数(跳过奇数)
printf("1-10的偶数: ");
for (int i = 1; i <= 10; i++) {
if (i % 2 != 0) {
continue; // 跳过奇数,直接进入下一次循环
}
printf("%d ", i); // 仅偶数会执行此句
}
printf("\n");
// 示例2:处理非零数字(跳过零值)
printf("处理非零数字(翻倍输出): ");
int numbers[] = {1, 0, 3, 0, 5};
for (int i = 0; i < 5; i++) {
if (numbers[i] == 0) {
continue; // 跳过0,不处理
}
printf("%d ", numbers[i] * 2); // 输出:2 6 10
}
printf("\n");
return 0;
}
(3)goto:无条件跳转(谨慎使用)
- 作用:无条件跳转到代码中指定的“标签”(标签格式:
标签名:); - 适用场景:多层循环快速退出、错误处理(替代多层break);
- 注意:滥用
goto会导致代码逻辑混乱(“面条代码”),仅在必要时使用。
#include <stdio.h>
int main() {
printf("=== goto语句(错误处理示例) ===\n");
int a, b;
printf("请输入两个非零整数: ");
scanf("%d %d", &a, &b);
// 错误处理:若有一个为0,跳转到错误处理标签
if (a == 0 || b == 0) {
goto error_handler; // 跳转至error_handler标签
}
// 正常逻辑:计算除法
printf("%d / %d = %.2f\n", a, b, (double)a / b);
goto end; // 跳过错误处理,直接到程序结束
error_handler: // 标签(必须单独一行,后跟冒号)
printf("错误:除数不能为零!\n");
end: // 程序结束标签
printf("程序结束\n");
return 0;
}
三、总结
1. 选择结构技巧
- 范围判断用
if-else if,固定值匹配用switch-case(效率更高); switch-case中,每个case后必加break,避免无意穿透;- 嵌套
if不超过3层(否则代码可读性差,可拆分为函数)。
2. 循环结构技巧
- 已知循环次数用
for,未知次数用while,必须执行一次用do-while; - 循环体内必须有“改变条件的语句”,避免无限循环;
- 多层循环中,用
break退出内层,goto(谨慎使用)退出多层; - 遍历数组时,用
for (int i = 0; i < len; i++)(结构化清晰)。
3. 注意事项
- 条件表达式中,避免用
=(赋值)代替==(等于); scanf读取字符时,注意吸收前一个输入的回车符(加空格%c);- 循环控制语句(
break/continue)仅影响当前层循环; - 处理数值计算时,先判断边界条件(如除零、数组越界)。
318

被折叠的 条评论
为什么被折叠?



