循环结构与for语句 | Loop Structure and For Statement
一、回顾与引入
在上一章中,我们学习了分支结构和条件语句:
if语句:根据条件选择是否执行if-else语句:根据条件选择执行不同的代码块else-if语句:处理多重条件判断
通过条件语句,我们的程序已经能够根据不同情况做出不同的反应。但是,在实际编程中,我们经常需要重复执行某些操作。
想象以下场景:
- 打印1到100的所有数字
- 计算1+2+3+...+100的和
- 输入100个学生的成绩并计算平均分
- 重复询问用户输入直到输入正确
如果用我们之前学过的知识,我们可能需要写100行重复的代码!这显然不现实。
今天我们要学习循环结构,让程序能够高效地重复执行代码,就像工厂的流水线一样。
二、什么是循环结构?
循环结构就是让程序重复执行某段代码,直到满足某个条件为止。
我们不妨用一个例子来理解循环的基本概念:
我如何连续喊出100次
Hello, World!?
程序如何连续输出100次Hello, World!\n?
这两个问题本质上是一样的。我们先来看第一个问题:
要喊出100次Hello, World!,我们自然需要在内心默数。这个过程有这样几个关键要素:
- 每次执行的动作(喊出"Hello, World!")
- 内心的计数器(从1数到100,每执行一次动作就 +1)
- 何时停止(数到100就停止)
编程中的循环概念很类似,只是由计算机来完成这个"默数"和"执行"的过程。
于是我们抽象出这样几个概念:
- 循环体:每次循环,所需要执行的、具有重复性特征的语句。例如每次都
输出"Hello, World!"。 - 循环变量:在这个例子里,我们在心里“默数”记录“反复喊出了多少次
Hello, World!”,如果用一个变量来存储这个次数,那么它便代表“进行了多少次循环”,像这样与循环有关的变量我们称作循环变量。 - 循环条件:在循环中,总是涉及到“何时停止”的问题。控制循环是否继续进行的条件是循环条件,在这个例子里,“循环次数<=100”就是循环条件。
三、for循环详解
C语言中有三种主要的循环语句:for、while、do-while。我们先学习最常用的for循环。
1. for循环的基本语法
for (初始化; 条件判断; 更新操作) {
// 循环体:需要重复执行的代码
}
语法说明:
- 初始化:多数时候,在这里声明循环变量,并设置循环变量的初始值,只执行一次
- 条件判断:每次循环前都会检查,为真继续循环,为假结束循环
- 循环体:需要重复执行的代码块
- 更新操作:每次循环结束后执行,通常用来更新循环变量
2. for循环的执行流程
我们仍然通过一个例子来理解for循环:
for (int i = 1; i <= 5; i++) {
printf("Hello, World!");
}
让我们一步步看这个循环是如何执行的:
-
第一步:初始化 -
int i = 1- 计算机创建一个变量
i,赋值为1 - 就像你准备开始数数,先在心里想"我从1开始数"
- 计算机创建一个变量
-
第二步:判断条件 -
i <= 5- 计算机检查:i(现在是1) <= 5 吗?答案是"是"
- 就像你问自己"我数到5了吗?"答案是"还没有"
-
第三步:执行循环体 -
printf("Hello, World!");- 既然条件满足,就执行大括号里的代码
- 就像你喊出"Hello, World!"
-
第四步:更新变量 -
i++- 执行完循环体后,i增加1,现在i变成2
- 就像你在心里数"2"
-
重复步骤2-4
- 继续检查条件(2 <= 5),执行循环体,i变成3...
- 直到i变成6时,条件(6 <= 5)为假,循环结束
整个过程就是:初始化→判断→执行→更新→判断→执行→更新...直到条件不满足。
3. 简单示例:打印数字
//Print_Numbers.c
#include <stdio.h>
int main() {
printf("=== 打印1到10的数字 ===\n");
for (int i = 1; i <= 10; i++) {
printf("数字: %d\n", i);
}
printf("循环结束!\n");
return 0;
}
运行结果:
=== 打印1到10的数字 ===
数字: 1
数字: 2
数字: 3
数字: 4
数字: 5
数字: 6
数字: 7
数字: 8
数字: 9
数字: 10
循环结束!
这个例子与上一个例子相较,有所不同的是:我们在循环体中使用到了循环变量i。这是很常见的做法,因为循环变量本身也是变量,和普通变量一样可以赋值、写入表达式等。例如这个例子的循环体中,printf("数字:%d\n", i);就在每次输出时打印了当前次循环的循环次数。
四、for循环的常见用法
1. 计算累加和
//Sum_Calculation.c
#include <stdio.h>
int main() {
int sum = 0;
printf("计算1+2+3+...+100的和\n");
for (int i = 1; i <= 100; i++) {
sum = sum + i; // 也可以写成:sum += i;
}
printf("结果:1到100的和 = %d\n", sum);
return 0;
}
2. 计算阶乘
//Factorial.c
#include <stdio.h>
int main() {
int n;
long long factorial = 1;
// long long是C中的另一种数据类型,用于存储整数。
// 与int类型不同的是,long long可以存储的范围是
// -2^63 ~ 2^63 - 1
// 因此使用long long类型存储阶乘结果,防止“存不下”
printf("请输入一个正整数: ");
scanf("%d", &n);
if (n < 0) {
printf("错误:负数没有阶乘\n");
return 1;
// 还记得return么?
// 记得return 0;认为规定了什么样的含义么?
// 为什么这里return的返回值不是0?
}
for (int i = 1; i <= n; i++) {
factorial = factorial * i; // 也可以写成:factorial *= i;
// n的阶乘 = n * (n-1) * ... * 2 * 1
// 循环变量i从1开始,每次循环通过i++加1
// 并让阶乘结果乘上i,最后算得n的阶乘
}
printf("%d! = %lld\n", n, factorial);
return 0;
}
3. 灵活使用for循环
事实上,for语句对循环变量的控制是很灵活的:
- 循环变量不一定初始化为1
- 循环条件可以多样
- 更新操作也不一定是每次循环一定是自增1,可以是自减1,甚至自加2等等。
例如这个例子中的倒序循环和下个例子中的步长不为1的循环等等。
倒序循环
//Countdown.c
#include <stdio.h>
int main() {
printf("=== 倒计时程序 ===\n");
for (int i = 10; i >= 1; i--) {
printf("倒计时: %d\n", i);
}
printf("时间到!\n");
return 0;
}
步长不为1的循环
//Step_Loop.c
#include <stdio.h>
int main() {
printf("=== 打印所有偶数(2到20) ===\n");
for (int i = 2; i <= 20; i += 2) { // 每次增加2
printf("偶数: %d\n", i);
}
printf("\n=== 打印所有奇数(1到19) ===\n");
for (int i = 1; i <= 19; i += 2) { // 每次增加2
printf("奇数: %d\n", i);
}
return 0;
}
五、for循环的变体
1. 省略某些部分
还记得for循环的基本语法是:
for (初始化; 条件判断; 更新操作) {
// ...
}
事实上,初始化、条件判断、更新操作这三条语句是可以省略不写的。表示分别在执行初始化、判断循环条件或执行更新操作时,不执行任何语句。
值得说明的是,即使省略初始化、条件判断或更新操作,用以分隔的分号;是不能省略的。分号提示了初始化、条件判断和更新操作三条语句的位置。
例如:
//Loop_Variants.c
#include <stdio.h>
int main() {
int i;
// 省略初始化部分
// 这里我们在for循环外声明变量i,并初始化为1。因此,for循环的初始化环节留空了。
i = 1;
for (; i <= 5; i++) {
printf("i = %d\n", i);
}
printf("\n");
// 省略更新部分
// for循环在执行完循环体后,会执行“更新操作”部分的语句。
// 因此,我们亦可以把“更新操作”的语句放在循环体内的最后,
// 并在for循环的“更新操作”处留空。
// 例如:
for (i = 1; i <= 5;) {
printf("i = %d\n", i);
i++; // 在循环体内更新
}
// 尽管这样的写法是等价的,但代码的可读性变差,并不推荐。
return 0;
}
2. 多个变量控制
//Multiple_Variables.c
#include <stdio.h>
int main() {
printf("=== 同时控制多个变量 ===\n");
for (int i = 1, j = 10; i <= 5; i++, j--) {
printf("i = %d, j = %d, 和 = %d\n", i, j, i + j);
}
return 0;
}
运行结果:
=== 同时控制多个变量 ===
i = 1, j = 10, 和 = 11
i = 2, j = 9, 和 = 11
i = 3, j = 8, 和 = 11
i = 4, j = 7, 和 = 11
i = 5, j = 6, 和 = 11
六、嵌套循环
当我们需要处理二维的问题时,经常需要使用嵌套循环(循环中包含循环)。
1. 打印乘法表
//Multiplication_Table.c
#include <stdio.h>
int main() {
printf("=== 九九乘法表 ===\n");
for (int i = 1; i <= 9; i++) { // 外层循环:行
for (int j = 1; j <= i; j++) { // 内层循环:列
printf("%d×%d=%d\t", j, i, i*j);
}
printf("\n"); // 每行结束后换行
}
return 0;
}
运行结果:
=== 九九乘法表 ===
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
1×4=4 2×4=8 3×4=12 4×4=16
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25
...
2. 打印图案
//Pattern_Printing.c
#include <stdio.h>
int main() {
int rows = 5;
printf("=== 打印星号三角形 ===\n");
for (int i = 1; i <= rows; i++) { // 控制行数
// 打印空格
for (int j = 1; j <= rows - i; j++) {
printf(" ");
}
// 打印星号
for (int k = 1; k <= 2*i - 1; k++) {
printf("*");
}
printf("\n");
}
return 0;
}
运行结果:
=== 打印星号三角形 ===
*
***
*****
*******
*********
七、常见错误与注意事项
1. 死循环
// ❌ 错误:忘记更新循环变量
for (int i = 1; i <= 10;) { // 缺少 i++
printf("%d\n", i);
// i永远是1,条件永远为真,形成死循环
}
// ❌ 错误:条件永远为真
for (int i = 1; i >= 1; i++) { // 条件错误
printf("%d\n", i);
// i会一直增长,条件永远满足
}
// ✅ 正确:
for (int i = 1; i <= 10; i++) {
printf("%d\n", i);
}
2. 循环变量的作用域(极其重要!!)
//Variable_Scope.c
#include <stdio.h>
int main() {
// 循环变量(例如这里的i)
// 如果在for循环语句的“初始化”部分中声明,只能在for循环体内使用。
// 你可以认为,像下面写法里的循环变量i,只“属于”for语句。
for (int i = 1; i <= 5; i++) {
printf("循环内: i = %d\n", i);
}
// ❌ 错误:i在此处不可访问
printf("循环外: i = %d\n", i); // 编译错误
// 如果想在循环内外都可以使用,需要在循环外声明循环变量。
// 你可以认为这样声明的循环变量j,属于主函数main()下的所有语句。
// 因此循环内外都可以使用。
// ✅ 正确:在循环外声明变量
int j;
for (j = 1; j <= 5; j++) {
printf("循环内: j = %d\n", j);
}
printf("循环外: j = %d\n", j); // j = 6
return 0;
}
3. 循环次数的计算
// 常见的循环次数计算错误
// 这个循环执行几次?
for (int i = 0; i < 10; i++) { // 执行10次:i从0到9
// ...
}
for (int i = 1; i <= 10; i++) { // 执行10次:i从1到10
// ...
}
for (int i = 0; i <= 10; i++) { // 执行11次:i从0到10
// ...
}
for (int i = 1; i < 10; i++) { // 执行9次:i从1到9
// ...
}
八、实际应用:成绩统计程序
和之前的内容一样,我们仍然用一个小例子来复习一下本节内容。你能看懂每一条语句在执行什么样的功能么?
//Grade_Statistics.c
#include <stdio.h>
int main() {
int n;
float score, sum = 0, average;
float max_score = 0, min_score = 100;
int pass_count = 0; // 及格人数
printf("=== 学生成绩统计程序 ===\n");
printf("请输入学生人数: ");
scanf("%d", &n);
if (n <= 0) {
printf("学生人数必须大于0!\n");
return 1;
}
printf("请依次输入%d个学生的成绩:\n", n);
for (int i = 1; i <= n; i++) {
printf("第%d个学生成绩: ", i);
scanf("%f", &score);
// 输入验证
if (score < 0 || score > 100) {
printf("成绩无效,请输入0-100之间的数字!\n");
i--; // 重新输入这个学生的成绩
continue;
}
// 累加总分
sum += score;
// 更新最高分和最低分
if (score > max_score) {
max_score = score;
}
if (score < min_score) {
min_score = score;
}
// 统计及格人数
if (score >= 60) {
pass_count++;
}
}
// 计算平均分
average = sum / n;
// 输出统计结果
printf("\n=== 统计结果 ===\n");
printf("学生总数: %d人\n", n);
printf("总分: %.2f\n", sum);
printf("平均分: %.2f\n", average);
printf("最高分: %.2f\n", max_score);
printf("最低分: %.2f\n", min_score);
printf("及格人数: %d人\n", pass_count);
printf("及格率: %.2f%%\n", (float)pass_count / n * 100);
return 0;
}
九、动手练习
练习:水仙花数寻找器
编写程序找出所有的三位水仙花数。水仙花数是指一个三位数,其各位数字立方和等于该数本身。
例如:153 = 1³ + 5³ + 3³ = 1 + 125 + 27 = 153
//参考答案
//Narcissistic_Numbers.c
#include <stdio.h>
int main() {
printf("=== 三位水仙花数寻找器 ===\n");
printf("水仙花数是指各位数字立方和等于该数本身的三位数\n\n");
int count = 0;
for (int num = 100; num <= 999; num++) {
// 分解数字的各个位
int hundreds = num / 100; // 百位
int tens = (num / 10) % 10; // 十位
int units = num % 10; // 个位
// 计算各位数字的立方和
int sum = hundreds*hundreds*hundreds +
tens*tens*tens +
units*units*units;
// 判断是否为水仙花数
if (sum == num) {
count++;
printf("第%d个水仙花数: %d = %d³ + %d³ + %d³ = %d + %d + %d = %d\n",
count, num, hundreds, tens, units,
hundreds*hundreds*hundreds,
tens*tens*tens,
units*units*units,
sum);
}
}
printf("\n总共找到了%d个三位水仙花数。\n", count);
return 0;
}
下一章预告: 学会了for循环后,我们将继续学习while循环和do-while循环,了解不同循环结构的特点和应用场景,让你的编程技能更加全面!



Comments | NOTHING