流程控制语句
通过一些语句,控制程序执行的流程。主要包括条件语句、循环语句和跳转语句。
分类:
- 顺序结构:程序默认的执行顺序,从上到下依次执行。
- 选择结构(分支结构):根据条件选择执行不同的代码块。
- 循环结构:重复执行某段代码,直到满足终止条件。
1. 顺序结构
程序默认的执行顺序,从上到下依次执行。
c
// 代码按从上到下的顺序执行
int a = 10; // 第一步
int b = 20; // 第二步
int sum = a + b; // 第三步
printf("%d", sum); // 第四步
2. 选择结构(分支结构)
根据条件选择执行不同的代码块。
2.1 if语句系列
- 利用if语句对数据进行判断,从而执行不同的代码
2.1.1 单分支if
- 使用场景: 某些代码,在满足条件的时候才执行
- 格式:
c
if (条件) {
// 条件为真时执行的代码
}
- 流程:

- 代码
c
#include <stdio.h>
int main ()
{
// 判断变量age里记录的值,如果大于等于18,就打印"已成年"
// 1. 定义变量
int age = 20;
// 2. 判断
if (age >= 18) {
printf("已成年\n"); // 已成年
};
return 0;
}
- 注意:
- 在C语言中,如果判断的结果是一个数字,任何非0值都视为真,0视为假
c
if (0) {
printf("0为假,不执行哦");
};
- 如果语句块中只有一条语句,可以省略花括号{},但为了代码清晰和避免错误,建议始终使用花括号。
c
int age = 19;
if (age >= 18)
printf("已成年\n");
2.1.2 双分支if-else
- 格式:
c
if (条件) {
// 条件为真时执行的代码
} else {
// 条件为假时执行的代码
}
- 流程:

- 代码
c
#include <stdio.h>
int main ()
{
int score = 75;
if (score >= 60) {
printf("及格\n");
} else {
printf("不及格\n");
}
return 0;
}
2.1.3 多分支 else if
- 说明:
- 从上往下一次进行判断
- 只要有一个判断(条件)成立,就执行对应的代码
- 如果所有判断都不成立,则执行else的代码
- 格式:
c
if (条件1) {
// 条件1为真时执行的代码
} else if (条件2) {
// 条件2为真时执行的代码
} else {
// 以上条件都不满足时执行的代码
}
- 流程:

- 代码:对分数进行等级划分
c
#include <stdio.h>
int main() {
int score = 85;
// 多条件判断
if (score >= 90) {
printf("优秀\n");
} else if (score >= 80) {
printf("良好\n");
} else if (score >= 60) {
printf("及格\n");
} else {
printf("不及格\n");
}
return 0;
}
嵌套if语句
- 格式:
c
if (条件1) {
if (条件2) {
if (条件3) {
// 三层嵌套
}
}
}
- 代码
c
#include <stdio.h>
int main() {
int score = 85;
if (score >= 0 && score <= 100) {
if (score >= 90) {
printf("优秀\n");
} else if (score >= 80) {
printf("良好\n");
} else if (score >= 60) {
printf("及格\n");
} else {
printf("不及格\n");
}
} else {
printf("分数超出范围\n");
}
return 0;
}
2.2 switch语句
-
用于多分支选择,注意每个case后通常要加break,否则会继续执行下一个case。
-
作用: 把所有的选择一一列举出来,根据不同的条件任选其一。
-
执行流程:
- 首先计算表达式的值
- 依次和case后面的值进行比较,如果有对应的值,就会执行相应的代码,在执行的过程中,遇到break就会结束
- 如果所以的case后面的值和表达式都不匹配,就会执行 default 里面的代码,然后结束整个switch语句
-
格式:
c
switch (表达式) {
case 值1: // 拿表达式和值1做对比,两者相同就执行代码块1,两者不相同就跳过,表达式去下面的case后面的值做对比。
// 代码块1
break; // 退出循环
case 值2: // 上面表达式与值1不相同才执行这里
// 代码块2
break;
// 可以有多个case
default:
// 以上都不匹配时执行的代码
break;
}
- 细节说明:
- 表达式:计算结果只能为(字符/整数)
- case:值只能是(字符/整数)的字面量,不能是变量
- case:不允许重复
- break:表示中断,结束的意思,结束 switch 语句,不执行下面的内容
- default:所有情况都不匹配,执行该处内容
- 流程图

- 代码
c
#include <stdio.h>
int main ()
{
int week = 3;
switch(week)
{
case 1:
printf("跑步\n");
break;
case 2:
printf("游泳\n");
break;
case 3:
printf("慢走\n");
break;
case 4:
printf("动感单车\n");
break;
case 5:
printf("拳击\n");
break;
case 6:
printf("爬山\n");
break;
case 7:
printf("好好吃一顿\n");
break;
default:
printf("没有这个星期\n");
break;
}
return 0;
}
- default
- default子句不是必须的,如果不需要处理默认情况,可以省略。
- default子句可以出现在switch语句中的任何位置(开头、中间或结尾),但通常放在最后。
- 如果default不在最后,且没有break语句,则会继续执行后面的case或default,直到遇到break或switch结束。
代码:省略default子句,如果没有default且没有匹配的case,则跳过整个switch
c
switch (num) {
case 1:
printf("一");
break;
case 2:
printf("二");
break;
// 没有default,如果num不是1或2,什么都不做
}
代码:default子句放在前面
c
switch (num) {
default: // 放在开头
printf("其他");
break;
case 1:
printf("一");
break;
case 2:
printf("二");
break;
}
- case 穿透
- 首先还是根据小括号中表达式的结果去匹配对应的case
- 执行对应case里面的代码
- 如果执行过程中,遇到break,那么直接结束整个switch
但是如果没遇到break,就会继续执行下面的case中的代码
知道遇到break,或者把switch中所有的代码全部执行完了,才会结束 - case穿透只会往下,不会回到上面,执行上面的代码
代码 - 多个case共享同一个代码块。
c
#include <stdio.h>
int main ()
{
int num = 1;
switch(num)
{
case 1: // num 匹配到数字 1,执行代码
printf("数字是1\n"); // 执行完,没有遇到break,继续执行下一个case的代码
case 2:
printf("数字是2\n"); // 执行代码
break; // 遇到break,退出switch语句
default:
printf("数字是其他\n");
break;
}
return 0;
}
执行结果:

代码:输出对应月份的季节(多个case共享同一个代码块)
c
#include <stdio.h>
int main ()
{
// 1. 键盘录入月份
int month;
printf("请输入月份\n");
scanf("%d", &month);
// 2. 输出对应月份的季节
switch (month)
{
case 1:
case 2:
case 12:
printf("冬季\n"); // 1 2 12都执行这里
break;
case 3:
case 4:
case 5:
printf("春季\n");
break;
case 6:
case 7:
case 8:
printf("夏季\n");
break;
case 9:
case 10:
case 11:
printf("秋季\n");
break;
default:
printf("输入月份错误\n");
break;
}
return 0;
}
代码:穿透的高级用法
c
// 根据权限级别执行累积操作
int permissions = 0;
int level = 3;
switch (level) {
case 3: // 最高权限
permissions |= ADMIN_PERM;
// 穿透,继续添加更多权限
case 2: // 中级权限
permissions |= WRITE_PERM;
// 穿透
case 1: // 基础权限
permissions |= READ_PERM;
break;
default:
permissions = NO_PERM;
break;
}
// level=3时:permissions包含所有权限
// level=2时:permissions包含WRITE和READ
// level=1时:permissions只有READ
穿透特性是C语言switch语句的一个强大但危险的功能。正确使用可以提高代码简洁性,滥用则会导致难以调试的错误。
2.3 switch 和 if 的区别
| 特性 | if语句 -- | switch语句 |
|---|---|---|
| 条件类型 | 任意表达式,结果为0/非0 | 整型或字符型表达式 |
| 比较方式 | 关系运算、逻辑运算等 | 与常量值相等比较 |
| 多分支 | 使用else if | 使用case标签 |
| 默认分支 | else | default |
| 穿透 | 无 | 有(不加break会穿透) |
| 可读性 | 条件复杂时较好 | 多常量比较时较好 |
| 效率 | 可能需要多次判断 | 通常使用跳转表,效率高 |
3. 循环结构
循环语句用于重复执行一段代码,直到满足终止条件。
- 构成:开始条件,结束条件,控制开关,循环体语句
- 循环结构包含:for 循环,while 循环,do...while 循环
3.1 for循环
- 语法:
c
for (初始化语句; 条件判断语句; 条件控制语句) {
// 循环体语句
}
- 代码:
c
#include <stdio.h>
int main ()
{
// 在VC编辑器中,初始化声明要放在上面。
int i;
// i = 1; : 初始化语句, 循环开始的条件。
// 在循环开始前执行一次,通常用于初始化循环变量。
// i <= 3; : 条件判断语句,循环结束的条件。
// 每次循环开始前都会检查这个条件,如果为真(非0),则执行循环体;如果为假(0),则终止循环。
// i++ :条件控制语句。控制循环的次数。
// 每次循环体执行后执行,通常用于更新循环变量。
// printf("吃西瓜的年年\n"); : 循环体语句,需要重复执行的语句。
for(i = 1; i <= 3; i++)
{
printf("吃西瓜的年年\n");
}
return 0;
}
- 执行流程:
- 执行初始化语句(只执行一次)
- 执行条件判断语句,如果为真,执行循环体;如果为假,跳出循环。
- 执行循环体
- 执行条件控制语句
- 回到2,直到条件表达式为假

以上面代码为例子
第一次循环:

第二次循环

第三次循环

第四次循环

我们发现进行循环的是后面这三段代码

- 总结:
- 初始化语句只执行一次
- 判断语句成立,循环继续
- 判断语句不成立,循环结束
- 练习
- 打印 5-1
c
#include <stdio.h>
int main ()
{
// 打印5-1
// 技巧:
// 1. 循环开始条件 i = 5
// 2. 循环结束条件 i >= 1
// 3. 重复执行的事情
// 4. 每次循环结束之后,变量i如何改变 i--
// 第一次循环 i = 5
// 第二次循环 i = 4
// 第三次循环 i = 3
// 第四次循环 i = 2
// 第五次循环 i = 1
int i;
for(i = 5; i >= 1; i--)
{
printf("%d\n", i);
}
return 0;
}
- 求和 1-5之间的和
c
#include <stdio.h>
int main ()
{
int i;
// 定义一个变量用来累加数据
int sum = 0;
// 执行流程
/*
在循环中,获取1-5的每一个数
第一次循环中:i = 1, sum = 0,循环体执行:sum = 0 + 1,sum = 1
第二次循环中:i = 2, sum = 1,循环体执行:sum = 1 + 2,sum = 3
第三次循环中:i = 3, sum = 3,循环体执行:sum = 3 + 3,sum = 6
第四次循环中:i = 4, sum = 6,循环体执行:sum = 6 + 4,sum = 10
第五次循环中:i = 5, sum = 10,循环体执行:sum = 10 + 5,sum = 15
...
*/
for (i = 1; i <= 5; i++)
{
sum = sum + i;
}
printf("%d\n", sum); // 15
return 0;
}
扩展知识:作用域:{}
在C语言中,一对花括号 {}定义了一个代码块,而一个代码块就是一个独立的作用域。
- 在函数内部,{}内定义的变量(局部变量)其作用域从定义点开始,到所在代码块结束为止。
- 不同代码块(即使是嵌套的)中可以定义同名变量,内层变量会暂时"遮蔽"外层变量。
- 代码块通常用于控制语句(如 if, for, while)的函数体,或者单独使用以限定变量的生命周期。
生命周期:变量只在所属的大括号中有效
c
#include <stdio.h>
int main ()
{
if (1) {
// 这里生命的 a 只在当前大括号内生效
// {} 是一个作用域,在里面声明的变量只能在里面生效。
// {} 里面代码运行结束后,就会把变量销毁,外面无法再获取里面的变量
int a = 10;
printf("%d\n", a); // 10
}
printf("%d\n", a); // 会报错
return 0;
}
可获取外部的变量
c
#include <stdio.h>
int main ()
{
int b = 20;
if (1) {
int a = 10;
printf("%d\n", a); // 10
// 获取外部的变量能打印出来
// 先看里面有么有变量b, 没有就会往作用域外部去寻找,找到了就拿来用
printf("%d\n", b); // 20
}
printf("%d\n", a); // 会报错
printf("%d\n", b); // 20
return 0;
}
// main 函数 执行完才会把 变量b 销毁
变量遮蔽:内层作用域可以定义与外层同名的变量,此时内层变量会暂时"覆盖"外层变量,退出内层作用域后,外层变量恢复可见。
c
#include <stdio.h>
int main ()
{
// main 函数 声明了一个 变量 a
int a = 20;
if (1) {
// if 语句声明了一个 变量 a
// 这里的 a 覆盖外面的变量 a,
// 或者说 虽然名字相同,但存储是两个地方,所以这只是名字相同的两个变量
// if 语句里开辟的作用域(空间),读取的是里面的变量,既然读取到里面的变量,就不会往外层去寻找
// 如果里面读取不到变量才会去外层找;
int a = 10;
printf("%d\n", a); // 10
}
// 外部作用域无法读取 if 里面的,因为 if 执行完,里面的变量已经销毁
// 所以它读取的是 main 函数 声明变量 a
printf("%d\n", a); // 20
return 0;
}
所以,上面代码的 sum 可以被循环用来累计。它不会被销毁。如果写在for循环里,那么循环一次就会被销毁一次,无法用来累计。
c
#include <stdio.h>
int main ()
{
int i;
// 执行流程
/*
在循环中,获取1-5的每一个数
第一次循环中:i = 1, sum = 0,循环体执行:sum = 0 + 1,sum = 1
当第一次循环后,变量 sum 就会从程序消失
第二次循环中:i = 2, sum = 0,循环体执行:sum = 0 + 2,sum = 2
当第一次循环后,变量 sum 就会从程序消失
...
*/
for (i = 1; i <= 5; i++)
{
// 定义一个变量用来累加数据,然后你会发现定义在for循环里面累加不了
int sum = 0;
sum = sum + i;
}
printf("%d\n", sum); // 报错,没有 sum 这个变量
return 0;
}
- 键盘录入两个数,算出他们即能被6整除又能被8整除的个数
c
#include <stdio.h>
int main24 ()
{
// 循环的变量
int i;
// 定义用来统计的个数的变量
int quantity = 0;
// 键盘录入的两个数字
int num1;
int num2;
// 最大值和最小值的变量声明
int max;
int min;
printf("请输入两位整数:");
scanf("%d %d", &num1, &num2);
// 比较两个值的大小
min = num1 < num2 ? num1 : num2; // 哪个小取哪个
max = num1 > num2 ? num1 : num2; // 哪个大取哪个
// 变量范围中的每一个数字
for (i = min; i <= max; i++)
{
// 判断是否能被6整除,和被8整除。用取余的方式,余数为0则能被整除
if (i % 6 == 0 && i % 8 == 0) {
// 符合要求在,则 quantity + 1
quantity++;
}
}
printf("既能被6整除,又能被8整除的个数有:%d个\n", quantity); // 4
return 0;
}
3.2 while循环
先判断条件,条件为真则执行循环体。
- 语法:
c
初始化语句
while (条件判断语句 / 循环条件) {
// 循环体
// 条件控制语句
}
- 代码:
c
#include <stdio.h>
int main ()
{
// 初始化语句
int i = 1;
while (i <= 3) // i <= 3: 条件判断语句,符合条件才执行循环体
{
printf("吃西瓜的年年\n"); // 循环体:要执行的代码
i++; // 条件控制语句,避免无限循环(无限循环后面会讲到)
}
return 0;
}
- 执行流程:和for循环一样,只是写法不一样

- 练习
- 求某个数是否为2的幂次方,是打印 yes,不是打印 no
c
#include <stdio.h>
int main ()
{
// 1.求是否为2的幂次方
/*
规律:
2的0次方 1
2的1次方 2
2的2次方 4
2的3次方 8
2的4次方 16
2的5次方 32
2的6次方 64
* 任意一个数字不断除以2,最终结果都会等于 1
循环结束的条件
1. 结束是1的时候,就可以结束,yes
2. 结果无法被2整除了,就可以结束,no
*/
int num = 100;
// 用 while 来循环,不断除以2
// 小括号里的为 继续执行的条件,即继续循环的条件
// 能被整除 num % 2 == 0,且这个数要大于 1。
// 如果这个数等于1,那就满足2的0次方。
// "2的幂次方"通常指的是2的非负整数次幂,即正整数,所以要 > 1。
while (num > 1 && num % 2 == 0)
{
// 不断除以2并赋值
num = num / 2;
}
// 判断,最终数字为1,则是2的幂次方
if (num == 1) {
printf("yes\n");
} else {
printf("no\n");
}
return 0;
}
- 世界最高山峰是珠穆朗玛峰(8844.43米=8844430毫米),假如我有一张足够大的纸,它的厚度是0.1毫米。我折叠多少次,可以折成珠穆朗峰的高度?
c
#include <stdio.h>
int main ()
{
// 声明珠穆朗峰高度和纸张的厚度
int height = 8844430;
double paper = 0.1;
// 计算折叠次数
int count = 0;
// 继续循环的条件:当paper的高度没有穆朗峰高度高时
while(paper < height)
{
// 折叠一次,纸张厚度乘以2
paper = paper * 2; // 或写成 paper *= 2
// 折叠次数 + 1
count++;
}
// 打印
printf("%d\n", count); // 27
return 0;
}
- 将一个整数进行反转,如:将 123 ----> 321
c
#include <stdio.h>
int main ()
{
// 3. 整数反转
/*
思路:依次获取每一位数,再拼接起来
1. 从右边开始依次获取每一个数 可以用取余,如: 123
第一次获取:123 % 10 = 3 (等于 12 余 3)
第一次获取:12 % 10 = 2 (等于 1 余 2)
第一次获取:1 % 10 = 1 (等于 0 余160 1)
2. 再把这三个数拼接起来
定义变量 int reverse = 0 用来拼接,公式:reverse = reverse * 10 加上获取的余数
第一次拼接 reverse = reverse(0) * 10 + 3 // 3
第二次拼接 reverse = reverse(3) * 10 + 2 = // 32
第二次拼接 reverse = reverse(32) * 10 + 1 = // 321
*/
int number = 123456;
int reverse = 0;
// 循环条件:number 不等于 0,就是还没除到最后
while (number != 0)
{
// 取余,取出最右边的数
int temp = number % 10;
// 去掉刚刚获取的数字
number = number / 10;
// 把余数拼接到reverse中
reverse =reverse * 10 + temp;
}
printf("%d\n", reverse); // 654321
return 0;
}
3.3 while 和 for 的对比
相同点:运行规则都一样
不同点:
- 写法不一样
- 使用场景:
- for循环:通常用于已知循环次数的情况,例如遍历数组或固定次数的迭代。由于初始化、条件和更新集中,代码可读性更高。
- while循环:更适合循环次数未知或依赖动态条件的情况,例如读取输入直到特定值出现。它更灵活,但需要额外管理循环变量。
3.4 do-while循环
先执行一次循环体,然后判断条件,条件为真则继续循环。
c
初始化语句
do {
// 循环体
// 至少执行一次
} while (循环条件);
- 流程

c
#include <stdio.h>
int main ()
{
// 打印 1- 5
int i = 1;
do
{
// 先执行
printf("%d\n", i);
i++;
} while(i <= 5); // 再判断
return 0;
}
这个很少用,知道有这个东西就可。
4. 跳转语句
- 跳转语句用于改变程序的正常执行流程。
- 再循环的过程中,跳转到其他语句上执行
4.1 break 语句
-
不能单独写,只能写在循环或switch语句中,表示结束,跳出当前循环或switch。
-
就是循环到某个条件时,不需要后面的循环,就用 break 直接结束循环
-
代码:在数字1-100中,找出第一个既能够被3整除,又能被5整除的数字
c
int main ()
{
int i;
for (i = 1; i<= 100; i++)
{
if (i % 3 == 0 && i % 5 == 0) {
printf("%d\n", i); // 15
// 找到后退出循环,不再循环,执行for循环下面的代码
break;
}
}
return 0;
}
4.2 continue 语句
-
用于跳过当前循环的剩余代码,直接进行下一次循环的条件判断(在while和do-while中)或更新表达式(在for循环中)。
-
结束本循环,继续下次循环
-
代码:打印数字1-5,遇到3不打印
c
#include <stdio.h>
int main ()
{
int i;
for (i = 1; i<= 5; i++)
{
if (i == 3) {
// 不执行下面打印代码(跳过本次循环),继续下一次循环
continue;
}
printf("%d\n", i);
}
return 0;
}
4.3 return 语句
用于从函数中返回,可以携带返回值(如果函数返回类型非void)。等学到函数再详解
5. 循环进阶
5.1 无限循环
- 在C语言中,无限循环通常用于需要一直运行直到满足特定条件(如用户输入、信号触发等)才退出的情况。
- 退出无限循环:关闭控制台 或 CTRL + C
while无限循环
c
// 循环条件 1 代表 true,会一直有效,不可省略
while(1) {
// 循环体
printf("吃西瓜的年年\n");
}
for无限循环
c
for(;;) {
// 循环体
printf("吃西瓜的年年\n");
}
do-while无限循环
c
do {
// 循环体
printf("吃西瓜的年年\n");
} while(1);
- 无限循环因为永远停不下来,所以下面不能再写其他代码
c
for(;;) {
// 循环体
printf("吃西瓜的年年\n");
}
printf("这一句打印不出来,因为上面在无限循环\n");
break 跳出无限循环
- 在无限循环中,通常需要在循环体内设置跳出循环的条件,否则程序将永远执行下去。例如,可以使用break语句在满足某个条件时跳出循环。
c
#include <stdio.h>
int main() {
char ch;
while(1) {
printf("请输入一个字符(输入'q'退出):");
scanf(" %c", &ch);
if(ch == 'q') {
break;
}
printf("你输入了:%c\n", ch);
}
return 0;
}
运行结果:

总结:
- 最常用:while(1)和 for(;😉
- 一定要有退出机制,否则程序无法正常终止
- 实际应用:无限循环常用于服务器、游戏主循环、事件监听等场景
5.2 嵌套循环
- 在C语言中,嵌套循环是指在一个循环体内包含另一个循环。
- 嵌套循环的执行顺序是:外层循环每执行一次,内层循环会完整地执行一遍。
- 语法:
c
for (外层初始化; 外层条件判断; 外层更新) {
for (内层初始化; 内层条件判断; 内层更新) {
// 循环体
}
}
- 代码
- 打印三行五列的
*,效果如下:
*****
*****
*****
c
#include <stdio.h>
int main ()
{
// 我们先来看看一行 * 怎么打印
// 是这样吗
// printf("*****\n");
// 难道,莫非你是个天才?
// 但我们想要灵活点,可以用for循环来打印
int i;
for (i = 1; i <= 5; i++)
{
printf("*");
}
printf("\n");
return 0;
}
打印结果:

那么我们要打印三行,就只需把上面的代码循环三次就可以
c
int j;
for (j = 1; j <= 3; j++) {
// 放入上面代码
}
完整代码
c
#include <stdio.h>
int main ()
{
// for嵌套循环
// 先确定内循环做了什么
// 外循环就是把里面的代码重复了几次
int i;
int j;
// 外循环
for (j = 1; j <= 3; j++) {
// 内循环
for (i = 1; i <= 5; i++) {
printf("*");
}
printf("\n");
}
return 0;
}
很好,完美结束!让我们来看下一题。
-
打印两个5行5列的三角形,效果如下:
*****
****
***
**
*和
*
**
***
****
*****
先学习第一个三角形。
c
#include <stdio.h>
int main ()
{
int i;
int j;
// 1.先打印出矩形
int i;
int j;
for (j = 1; j <= 5; j++) {
for (i = 1; i <= 5; i++) {
printf("*");
}
printf("\n");
}
return 0;
}

当我们打印矩形,下一步我们就要给每一行递减一个 *,决定每一行打印多少个的是在内循环,请看内循环
c
#include <stdio.h>
int main ()
{
// 1.先打印出矩形
int i;
int j;
// 内循环代码解析
// i = 1:打印第一行的*;内循环初始值:1,循环条件判断:5,5个*
// i = 2:打印第二行的*;内循环初始值:2,循环条件判断:5,4个*
// i = 3:打印第三行的*;内循环初始值:3,循环条件判断:5,3个*
// i = 4:打印第四行的*;内循环初始值:4,循环条件判断:5,2个*
// ...
for (j = 1; j <= 5; j++) {
// 决定了每一行打印多少个*
// 5 4 3 2 1
// 内循环初始化的值 = j
for (i = j; i <= 5; i++) {
printf("*");
}
printf("\n");
}
return 0;
}

学习第二个三角形,同样先打印矩形,再看看详解
c
#include <stdio.h>
int main ()
{
// 1.先打印出矩形
int i;
int j;
// 内循环代码解析
// i = 1:打印第一行的*;内循环初始值:1,循环条件判断:1,1个*
// i = 2:打印第二行的*;内循环初始值:1,循环条件判断:2,2个*
// i = 3:打印第三行的*;内循环初始值:1,循环条件判断:3,3个*
// i = 4:打印第四行的*;内循环初始值:1,循环条件判断:4,4个*
// ...
for (j = 1; j <= 5; j++) {
// 决定了每一行打印多少个*
// 1 2 3 4 5
// 改变条件判断,递增
for (i = 1; i <= j; i++) {
printf("*");
}
printf("\n");
}
return 0;
}

- 打印九九乘法表

用上面的 * 的循环来进行改造。我们把它改成九行
c
#include <stdio.h>
int main ()
{
int i;
int j;
// 循环条件改成 9
for (j = 1; j <= 9; j++) {
for (i = 1; i <= j; i++) {
printf("*");
}
printf("\n");
}
return 0;
}

目前已经实现了样式,现在就来看看内容。我们不再打印 * 了,要打印数字。
我们要打印的数字都是 1-9,我们发现, i和 j 每次循环也是1 -9 ,我们把他们拿来试试填进去看看
c
#include <stdio.h>
int main ()
{
int i;
int j;
// 循环条件改成 9
for (j = 1; j <= 9; j++) {
for (i = 1; i <= j; i++) {
// 这里改成我们要打印的乘法
printf("%d * %d = %d ", j, i, i * j);
}
printf("\n");
}
return 0;
}
看看效果

看起来 i 和 j 的位置不对,我们把他们换一下位置就好
c
#include <stdio.h>
int main ()
{
int i;
int j;
// 循环条件改成 9
for (j = 1; j <= 9; j++) {
for (i = 1; i <= j; i++) {
// 把这里 j 和 i位置换一下
printf("%d * %d = %d ", i, j, i * j);
}
printf("\n");
}
return 0;
}
再次打印

好像没啥问题,但有些地方对不起,看着不大舒服。
我们换成 \t 制表符(Tab),让它的长度对齐变大
- 原理:会根据前面字母的个数在后面补空格,让整体的长度达到8或者8的倍数,最少补1个,最多补8个
- 作用:打印表格数据的时候,可以对齐
c
#include <stdio.h>
int main ()
{
int i;
int j;
// 循环条件改成 9
for (j = 1; j <= 9; j++) {
for (i = 1; i <= j; i++) {
// 把最后面的空格换个 \t
printf("%d * %d = %d\t", i, j, i * j);
}
printf("\n");
}
return 0;
}

5.3 goto 语句
- 在C语言中,goto语句是一种无条件跳转语句,它允许程序跳转到同一函数内的任何位置。常用来跳出循环。
- 过度使用goto语句会导致程序结构不清晰,难以理解和维护。
- 现代编程中一般建议尽量避免使用goto语句,而是使用结构化的控制语句(如if、while、for等)来代替。
- 语法:
c
goto label;
...
label: statement;
- 代码:
- 嵌套循环
c
#include <stdio.h>
int main ()
{
int i;
int j;
// 外循环
for (i = 1; i <= 3; i++) {
// 内循环
for(j = 1; j <= 5; j++) {
printf("内循环执行%d\n", j);
}
printf("内循环结束\n");
printf("----------------\n");
}
printf("外循环结束\n");
return 0;
}

我们在内循环使用 goto 来中止循环,看看会发生什么
c
#include <stdio.h>
int main ()
{
// 使用 goto 语句退出循环
int i;
int j;
// 外循环
for (i = 1; i <= 3; i++) {
// 内循环
for(j = 1; j <= 5; j++) {
printf("内循环执行%d\n", j);
// 循环完一次后就结束了
goto a; // 跳转到 a 标签
}
printf("内循环结束\n");
printf("----------------\n");
}
// 标签名定义
a: printf("外循环结束\n");
return 0;
}

这里直接跳出内外循环,再看看下面的,我们换个位置
c
#include <stdio.h>
int main ()
{
// 使用 goto 语句退出循环
int i;
int j;
// 外循环
for (i = 1; i <= 3; i++) {
// 内循环
for(j = 1; j <= 5; j++) {
printf("内循环执行%d\n", j);
goto a; // 跳转到 a 标签
}
// 标签名定义
a: printf("内循环结束\n");
printf("----------------\n");
}
printf("外循环结束\n");
return 0;
}

这里只是跳出了内循环后面的内容,会把外循环执行完。
好,接下来看下内循环 break 是怎么跳转的
c
#include <stdio.h>
int main ()
{
int i;
int j;
// 外循环
for (i = 1; i <= 3; i++) {
// 内循环
for(j = 1; j <= 5; j++) {
printf("内循环执行%d\n", j);
break;
}
printf("内循环结束\n");
printf("----------------\n");
}
printf("外循环结束\n");
return 0;
}

我们发现放在内循环的 break,它终止的也是内循环,无法终止外循环,外循环还是会继续运行,直到结束。
- 向前跳转制造循环,不推荐
c
#include <stdio.h>
int main ()
{
int i = 0;
start:
if (i < 10) {
printf("%d ", i);
i++;
goto start; // 应该用for/while循环
}
return 0;
}

使用建议:
- 限制使用范围:只在同一函数内使用
- 只向后跳转:避免向前跳转(loop标签除外)
- 用于错误处理:这是goto最有价值的用途
- 避免用于普通流程控制:用if/else、for/while等代替
- 添加清晰注释:说明为什么使用goto
6. 总结
注意事项:
- 条件语句中,条件表达式的结果应为整型(0为假,非0为真)。
- 在循环语句中,注意避免死循环。
- 使用break和continue时,注意它们只影响最内层的循环。
- goto语句虽然灵活,但会破坏程序的结构,应尽量避免使用。
分类对比表:
