1.while循环
C语言中的while循环是一种常用的控制结构,用于重复执行某段代码,直到满足某个条件为止。
1.1.基本语法
while循环的基本语法如下:
while(条件){
//循环体
}
- 条件:一个布尔表达式,用于判断是否继续执行循环。
- 循环体:当条件为true时,执行的代码块。
执行流程:
- 首先计算“条件”的值。
- 如果“条件”为true,执行循环体。
- 执行完循环后,再次计算“条件”,重复步骤2。
- 如果“条件”为false,退出循环,执行循环后的代码。
1.2.注意事项
- 条件判断:while循环的条件必须是一个布尔表达式。如果条件始终为true,会导致死循环。
- 变量初始化:在循环开始前,确保相关变量已经被正确初始化。
- 循环变量更新:在循环体内,通常需要更新循环变量,以确保循环能够终止。
- 空循环:如果循环体为空,必须用 ; 表示空语句
- 应用场景:适用于需要重复执行某段代码,直到满足某个条件的场景。
1.3.示例演示
1.3.1.累加计算
#include <stdio.h>
int main() {
int sum = 0; // 初始化累加和
int i = 1; // 初始化计数器
while (i <= 10) { // 条件:i <= 10
sum += i; // 累加
i++; // 更新计数器
}
printf("Sum from 1 to 10 is: %d\n", sum);
return 0;
}
输出:
Sum from 1 to 10 is: 55
1.3.2.死循环
#include <stdio.h>
int main() {
while (1) { // 条件始终为true
printf("This is an infinite loop.\n");
// 按Ctrl+C可以终止程序
}
return 0;
}
1.3.3.嵌套while循环
#include <stdio.h>
int main() {
int i = 1; // 外层循环计数器
while (i <= 3) {
int j = 1; // 内层循环计数器
while (j <= 2) {
printf("(%d, %d) ", i, j);
j++; // 更新内层计数器
}
i++; // 更新外层计数器
}
return 0;
}
输出:
(1, 1) (1, 2) (2, 1) (2, 2) (3, 1) (3, 2)
2.do-while循环
C语言中的do-while循环是一种后测试循环,它的特点时循环体至少会执行一次,然后再判断条件是否满足继续执行。这种循环结构在某些场景下非常有用,比如需要确保某些代码至少执行一次的场景。
2.1.基本语法
do-while循环的语法如下:
do {
// 循环体
} while (条件);
- 循环体:必须放在do和while之间,并用花括号{}包裹。
- 条件:一个布尔表达式,用于判断是否继续执行循环。
- 分号:while后面必须有一个分号 ; ,否则会导致语法错误。
执行流程:
- 首先执行循环体。
- 然后计算条件的值。
- 如果条件为true,重复步骤1;否则退出循环。
2.2.注意事项
- 核心特点:无论条件是否为true,循环体都会执行一次。
- 条件判断:条件必须是一个布尔表达式。如果条件始终为true,会导致死循环。
- 循环变量更新:在循环体内,通常需要更新循环变量,以确保循环能够终止。
- 分号不可省略:while后面必须有一个分号 ; ,否则会导致编译错误。
- 空循环体:如果循环体为空,必须用空语句{ }表示。
2.3.与while循环的对比
特性 | do-while | while |
执行顺序 | 先执行循环体,再判断条件 | 先判断条件,再决定是否执行循环体 |
适用场景 | 至少执行一次的操作(如输入验证) | 条件可能为假但需跳过循环的情况 |
2.4.示例演示
2.4.1.嵌套do-while循环
#include <stdio.h>
int main() {
int i = 1; // 外层循环计数器
do {
int j = 1; // 内层循环计数器
do {
printf("(%d, %d) ", i, j);
j++; // 更新内层计数器
} while (j <= 2);
i++; // 更新外层计数器
} while (i <= 3);
return 0;
}
输出:
(1, 1) (1, 2) (2, 1) (2, 2) (3, 1) (3, 2)
2.4.2.输入验证(强制合法输入)
#include <stdio.h>
int main() {
int num;
do {
printf("请输入一个正整数:");
scanf("%d", &num);
} while (num <= 0); // 若输入非法值,重复提示输入
printf("输入有效:%d\n", num);
return 0;
}
注:用户必须输入大于0的整数才能退出循环
2.4.3.逆向显示整数值
#include <stdio.h>
int main() {
int number = 1234;
do {
printf("%d", number % 10); // 取末位数字
number /= 10; // 移除末位
} while (number > 0); // 输出:4321
return 0;
}
注:若number初始为0,循环体仍会执行一次,输出0
3.for循环
for循环是C语言中最常用的循环结构之一,特别适合用于需要明确循环次数的场景。
3.1.基本语法
for循环的语法如下:
for (初始化; 条件判断; 更新表达式) {
// 循环体
}
- 初始化:在循环开始前执行一次,通常用于初始化循环变量。
- 条件判断:每次循环开始前都会检查的条件,如果条件为true,继续执行循环体;否则退出循环。
- 更新表达式:每次循环体执行完毕后执行,通常用于更新循环变量。
执行流程:
- 执行初始化部分。
- 检查条件判断的值。
- 如果条件为true,执行循环体。
- 执行更新表达式。
- 重复步骤2-4,直到条件为false。
3.2.注意事项
1. 初始化、条件判断和更新表达式:
- 这三个部分用分号 ; 分隔,缺一不可。
- 初始化部分通常用于声明和初始化循环变量。
- 条件判断部分是一个布尔表达式。
- 更新表达式通常用于递增或递减循环变量。
2. 空循环体:如果循环体为空,可以用 ; 表示空语句,例如:
for (i = 0; i < 10; i++);
3. 死循环:如果条件判断始终为true,会导致死循环。例如for (;;)。
4. 循环变量的作用域:在C语言中,for循环的初始化部分声明的变量作用域仅限于整个for循环。
5. 灵活性:for循环的三个部分可以为空,但分号不能省略。例如:
for (;;); // 死循环
3.3.示例演示
3.3.1.累加计算
#include <stdio.h>
int main() {
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
}
printf("Sum from 1 to 10 is: %d\n", sum);
return 0;
}
输出:
Sum from 1 to 10 is: 55
3.3.2.打印乘法表
#include <stdio.h>
int main() {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
printf("%d*%d=%d ", j, i, i * j);
}
printf("\n");
}
return 0;
}
输出:
3.3.3.计算1~100之间3的倍数的数字之和
#include <stdio.h>
int main(){
int i = 0;
int sum = 0;
for(i = 1; i <= 100; i++){
if(i % 3 == 0)
sum += i;
}
printf("%d\n", sum);
return 0;
}
可优化为:
//如果能直接产⽣3的倍数的数字就省去了多余的循环和判断
#include <stdio.h>
int main(){
int i = 0;
int sum = 0;
for(i = 3; i <= 100; i += 3){
sum += i;
}
printf("%d\n", sum);
return 0;
}
4.break语句
在C语言中,break 语句是一个重要的流程控制语句,主要用于中断循环或switch语句的执行。
4.1.基本用法
4.1.1.在switch语句中使用
- 用于防止case穿透(fall-through)
- 执行到break会立即退出整个switch结构
switch(grade) {
case 'A':
printf("Excellent");
break; // 跳出switch
case 'B':
printf("Good");
break;
default:
printf("Invalid");
}
4.1.2.在循环结构中使用
- 用于提前终止循环(while/for/do-while)
- 执行到break会立即跳出当前所在层的循环
for(int i=0; i<10; i++) {
if(i == 5) {
break; // 当i=5时终止循环
}
printf("%d ", i); // 输出0 1 2 3 4
}
4.2.注意事项
4.2.1.仅影响最近一层结构
- 在嵌套循环中,break只能跳出当前所在层的循环
for(int i=0; i<3; i++) {
for(int j=0; j<3; j++) {
if(j == 1) break; // 仅跳出内层循环
printf("(%d,%d) ", i, j);
}
}
// 输出:(0,0) (1,0) (2,0)
4.2.2.不能用于if语句
- 如果break出现在非循环/非switch的if语句中,会导致编译错误
4.2.3.慎用break
- 过度使用会降低代码可读性,建议优先使用合理的循环条件
4.2.4.switch中的break陷阱
- 忘写break会导致case穿透现象:
switch(n) {
case 1: printf("One");
case 2: printf("Two"); // 当n=1时会连续输出OneTwo
}
4.3.示例演示
4.3.1.查找数组元素
int arr[] = {2,4,6,8,10};
int target = 8;
for(int i=0; i<5; i++) {
if(arr[i] == target) {
printf("Found at index %d", i);
break; // 找到后立即退出循环
}
}
4.3.2.输入验证
while(1) { // 无限循环
printf("Enter 0 to exit: ");
int num;
scanf("%d", &num);
if(num == 0) {
break; // 用户输入0时退出循环
}
}
4.3.3.跳出多层循环
对于需要跳出多层循环的场景,可以使用以下方法:
int flag = 0;
for(int i=0; i<3; i++) {
for(int j=0; j<3; j++) {
if(condition) {
flag = 1;
break;
}
}
if(flag) break; // 通过标志变量跳出外层循环
}
4.4.最佳实践
- 在switch语句中始终添加break,除非明确需要穿透效果
- 在循环中使用break时,尽量让终止条件明显
- 对于复杂逻辑,优先考虑重构代码而不是过度使用break
5.continue语句
在C语言中,continue 语句是另一种关键的流程控制语句,专门用于 循环结构 中跳过当前迭代的剩余代码,直接进入下一次循环。
5.1.基本用法
5.1.1.核心作用
- 在循环(while/for/do-while)中跳过本次迭代的后续代码,立即开始下一次循环。
- 与break(完全终止循环)不同,continue仅跳过当前迭代。
5.1.2.语法形式
while(condition) {
// 代码A
if(skip_condition) {
continue; // 跳过代码B,直接回到循环条件判断
}
// 代码B
}
5.1.3.适用场景
- 过滤不需要处理的数据
- 提前结束某些特定条件下的操作
- 优化循环逻辑,避免深层嵌套
5.2.注意事项
5.2.1.循环类型影响行为
- for循环:continue会跳过循环体剩余代码,但仍执行增量表达式(如i++)
for(int i=0; i<5; i++) {
if(i == 2) continue;
printf("%d ", i); // 输出 0 1 3 4(i=2时跳过打印,但i仍会自增)
}
- while/do-while循环:continue直接跳转到循环条件检查,可能导致死循环:
int i = 0;
while(i < 5) {
i++;
if(i == 3) continue; // 若此处未修改循环变量,可能陷入死循环
printf("%d ", i);
}
5.2.2.不能用于非循环结构
- 在switch或纯if语句中使用continue会导致编译错误。
5.2.3.避免逻辑混淆
- 过度使用continue可能使代码可读性下降,需确保逻辑清晰。
5.3.示例演示
5.3.1.跳过奇数打印
for(int i=1; i<=10; i++) {
if(i % 2 != 0) {
continue; // 跳过奇数
}
printf("%d ", i); // 输出 2 4 6 8 10
}
5.3.2.输入验证过滤负数
int sum = 0, num;
for(int i=0; i<5; i++) {
printf("Enter positive number: ");
scanf("%d", &num);
if(num < 0) {
printf("Negative ignored!\n");
continue; // 跳过负数的累加
}
sum += num;
}
printf("Total: %d", sum);
5.4.常见陷阱
5.4.1.修改循环变量前使用 continue
int i = 0;
while(i < 5) {
if(i == 2) {
continue; // 死循环!i永远无法自增到3
}
i++;
}
5.4.2.误用 continue 代替条件判断
- 不推荐写法
for(...) {
if(condition) continue;
else {
// 复杂逻辑
}
}
- 推荐改写为:
for(...) {
if(!condition) {
// 清晰的条件分支
}
}
5.5.最佳实践
5.5.1.明确循环变量修改时机
- 在 while 循环中使用 continue 时,确保循环变量在 continue 前已更新。
5.5.2.优先使用正向逻辑
- 尽量减少 continue 的使用,改用正向条件判断以提高可读性:
// 不推荐
for(...) {
if(should_skip) continue;
// 业务代码
}
// 推荐
for(...) {
if(!should_skip) {
// 业务代码
}
}
5.5.3.注释说明跳过原因
- 对复杂的 continue 条件添加注释,说明跳过逻辑:
if(data == INVALID_FLAG) {
continue; // 跳过无效数据(协议规定0xFFFF为无效标记)
}
6.goto语句
goto语句是C语言中一种无条件跳转控制语句,允许程序跳转到同一函数内的指定标签处执行。尽管在现代编程中其使用备受争议,但在特定场景下仍具实用价值。
6.1.基本用法
6.1.1.语法结构
- 标签定义:在代码中通过标签名:定义跳转目标。
- 跳转指令:使用goto 标签名;实现跳转。
-
#include <stdio.h> int main() { printf("Start\n"); goto my_label; // 跳转到标签处 printf("Skipped\n"); my_label: // 标签定义 printf("Jumped here\n"); return 0; } /* 输出: * Start * Jumped here */
6.1.2.适用场景
- 错误处理与资源清理:在函数中多处错误退出时,集中释放资源。
- 跳出深层嵌套循环:替代多层break,简化代码逻辑。
- 状态机实现:有限状态机(FSM)中切换状态。
6.2.注意事项
6.2.1. 结构化编程限制
- 破坏代码逻辑:过度使用导致“面条代码”(Spaghetti Code),难以跟踪流程。
- 替代方案优先:优先使用break、continue、函数返回或错误码处理逻辑。
6.2.2. 作用域限制
- 同一函数内跳转:goto不能跨函数跳转。
- 变量初始化问题:不可跳过变量的初始化。
int main() {
goto label;
int x = 10; // 编译器报错:跳过初始化
label:
printf("%d", x);
return 0;
}
6.2.3.跳转目标合法性
- 禁止跨作用域跳转:不能从外部跳入循环、条件语句或switch内部。
for (int i=0; i<3; i++) {
inner:
printf("Loop\n");
}
goto inner; // 错误:跳入循环作用域
6.2.4.资源泄漏风险
未释放资源:跳转前需确保已释放已分配的内存、文件句柄等。
FILE *fp = fopen("file.txt", "r");
if (error) {
goto exit; // 错误:未关闭文件直接跳转
}
exit:
// 此处应添加 fclose(fp);
return;
6.3.示例演示
6.3.1.集中错误处理
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp1 = NULL, *fp2 = NULL;
fp1 = fopen("file1.txt", "r");
if (!fp1) {
perror("Open file1 failed");
goto cleanup; // 跳转到清理
}
fp2 = fopen("file2.txt", "w");
if (!fp2) {
perror("Open file2 failed");
goto cleanup; // 跳转到清理
}
// 正常业务逻辑...
cleanup: // 统一释放资源
if (fp1) fclose(fp1);
if (fp2) fclose(fp2);
return 0;
}
6.3.2.跳出多层嵌套循环
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
if (i == 2 && j == 3) {
goto exit_loops; // 直接跳出所有循环
}
printf("(%d,%d) ", i, j);
}
}
exit_loops:
printf("\nExited loops.\n");
/* 输出:
* (0,0) (0,1) ... (2,0) (2,1) (2,2)
* Exited loops.
*/
6.3.3.错误示范(死循环)
int x = 0;
start:
printf("%d ", x);
x++;
if (x < 5) goto start; // 合法但可读性差,等同于循环
// 输出:0 1 2 3 4