C控制语句:循环

掌握 for/while/do-while 三种循环结构、嵌套循环与哨兵值循环模式,能编写计数循环和条件驱动循环程序

学习目标

  • 能区分 forwhiledo-while 三种循环的语法与执行顺序,并选出适合计数循环、哨兵输入、至少执行一次菜单的场景
  • 能改出嵌套循环打印乘法表或遍历 3×3 矩阵的效果,并解释外循环与内循环各自控制什么
  • 能用哨兵值模式编写「读入数据直到遇到结束标记」的程序,并回答:如果把 while 换成 do-while 且哨兵是第一个读入的值,循环体会多执行几次?

机制总览

C控制语句:循环:机制路径

  1. 1

    为什么程序需要「反复执行」?

    想象一条巧克力工厂的流水线:同一道工序要对传送带上的每一块巧克力重复操作——称重、包膜、贴标签。工人不会只做一块就下班,而是「只要还有货,就继续干」。程序里的很多任务也一样:打印 1 到 100、累加用户输入的分数、遍历矩阵每个格子——都需要把同一段代码跑很多遍。

  2. 2

    while 回顾:先判后做的入口条件循环

    上一章你已经见过 while 循环 。它的规则很简单: 先问「还要不要继续?」,答「要」才干活。

  3. 3

    for 循环:把三要素写在一行

    猜一猜:下面 for 循环结束后, i 的值是多少?通过 Stepper 一步步揭晓。

先按顺序建立机制,再进入实验切换阶段并检查失效证据。

章级决策实验

C控制语句:循环:机制与证据

切换《C控制语句:循环》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。

选择推理阶段

当前阶段 · 为什么程序需要「反复执行」?

想象一条巧克力工厂的流水线:同一道工序要对传送带上的每一块巧克力重复操作——称重、包膜、贴标签。工人不会只做一块就下班,而是「只要还有货,就继续干」。程序里的很多任务也一样:打印 1 到 100、累加用户输入的分数、遍历矩阵每个格子——都需要把同一段代码跑很多遍。

可核验证据

用 -Wall -Wextra -pedantic 构建本节示例,再以正常值、边界值和失败输入核对「为什么程序需要「反复执行」?」的实际行为。

学完《C控制语句:循环》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。

失效—证据矩阵

C控制语句:循环:失效与核验

为什么程序需要「反复执行」?

典型失效

若只记语法而忽略「为什么程序需要「反复执行」?」的类型范围、求值顺序或资源边界,输入变化后可能出现未定义行为或错误结果。

核验证据

用 -Wall -Wextra -pedantic 构建本节示例,再以正常值、边界值和失败输入核对「为什么程序需要「反复执行」?」的实际行为。

while 回顾:先判后做的入口条件循环

典型失效

若只记语法而忽略「while 回顾:先判后做的入口条件循环」的类型范围、求值顺序或资源边界,输入变化后可能出现未定义行为或错误结果。

核验证据

用 -Wall -Wextra -pedantic 构建本节示例,再以正常值、边界值和失败输入核对「while 回顾:先判后做的入口条件循环」的实际行为。

for 循环:把三要素写在一行

典型失效

若只记语法而忽略「for 循环:把三要素写在一行」的类型范围、求值顺序或资源边界,输入变化后可能出现未定义行为或错误结果。

核验证据

用 -Wall -Wextra -pedantic 构建本节示例,再以正常值、边界值和失败输入核对「for 循环:把三要素写在一行」的实际行为。

每个判断都必须能落到观测、测试或产物,不能只凭代码表面推测。

为什么程序需要「反复执行」?

想象一条巧克力工厂的流水线:同一道工序要对传送带上的每一块巧克力重复操作——称重、包膜、贴标签。工人不会只做一块就下班,而是「只要还有货,就继续干」。程序里的很多任务也一样:打印 1 到 100、累加用户输入的分数、遍历矩阵每个格子——都需要把同一段代码跑很多遍。

如果没有循环呢? 你得把 printf 复制粘贴一百遍,改一百次数字。用户输入个数不确定时,你甚至不知道要粘贴多少遍。循环就是 C 语言给你的「流水线开关」:写一次逻辑,让机器按条件反复执行,直到该停的时候停。

这一章教你三种循环开关——forwhiledo-while——以及两种经典用法:数着跑(计数循环)和看着停(哨兵值循环)。

while 回顾:先判后做的入口条件循环

上一章你已经见过 。它的规则很简单:先问「还要不要继续?」,答「要」才干活。

while 循环流程图① 条件成立?开始② 执行循环体花括号内的代码③ 更新条件变量i++ / n-- / ...④ 回到判断退出循环菱形 = 条件判断 · 矩形 = 执行步骤 · 返回弧线 = 继续循环 · 向左分支 = 退出
while 循环的标准执行流程:先检查条件 → 条件为真执行循环体 → 更新条件变量 → 回到检查。条件为假时退出循环。

当输入数据的个数事先不知道时,常用 驱动循环——你不需要提前知道有多少个数字,只要约定「读到 -1 就表示结束」:

哨兵值循环模式① 读入一个值scanf("%d", &item);② 是哨兵值?item == -1否(继续)③ 处理该值④ 再读入结束哨兵值(如 -1)标记数据结束——先读再判,适合不知道输入个数的场景
哨兵值循环:每次先读入数据,再判断是否为结束标记(哨兵)。不是哨兵就处理并继续读入,是哨兵则退出。
#include <stdio.h>
int main(void) {
    int item;
    int sum = 0;
 
    printf("输入整数(-1 结束):\n");
    if (scanf("%d", &item) != 1) return 1;
 
    while (item != -1) {         // -1 是哨兵
        sum += item;
        if (scanf("%d", &item) != 1) return 1;
    }
 
    printf("总和 = %d\n", sum);
    return 0;
}

这种模式叫「先读入、再判断、再处理、再读入」——第一个数在循环外读,循环内每次处理完再读下一个。也有人把读入放在循环体开头,逻辑等价,只是写法不同。

for 循环:把三要素写在一行

把循环最需要关心的三件事——从哪开始、什么时候停、每轮怎么推进——压缩进括号里:

for (初始化; 条件; 更新) {
    // 循环体
}
for(init; cond; update) 流程图① 初始化 initint i = 0;② 条件 cond?③ 执行循环体④ 更新 updatei++回到②退出循环for 把 init / cond / update 写在一行——三要素各管一段,循环体只放花括号里
for 循环标准流程:初始化(只执行一次)→ 条件判断 → 循环体 → 更新 → 回到条件。条件为假时退出。

三要素各司其职:

  • 初始化(init):设置 ,只执行一次
  • 条件(cond):每轮开始前检查,为真才进入循环体
  • 更新(update):每轮循环体结束后执行,通常用 i++ 推动控制变量

猜一猜:下面 for 循环结束后,i 的值是多少?通过 Stepper 一步步揭晓。

分步1 / 4

第一步:初始化(只执行一次)

for(init; cond; update) 流程图① 初始化 initint i = 0;② 条件 cond?③ 执行循环体④ 更新 updatei++回到②退出循环for 把 init / cond / update 写在一行——三要素各管一段,循环体只放花括号里
for 循环标准流程:初始化(只执行一次)→ 条件判断 → 循环体 → 更新 → 回到条件。条件为假时退出。

int i = 0 在循环开始前执行一次——把计数器归零。这一步不属于「重复」的部分,for 的语法把它写在括号最左边,强调「循环从什么状态出发」。

典型计数循环——打印 1 到 10 的平方:

#include <stdio.h>
int main(void) {
    int i;
 
    for (i = 1; i <= 10; i++) {
        printf("%d^2 = %d\n", i, i * i);
    }
 
    return 0;
}

i = 1 起步,i <= 10 控制最后一轮是 10,i++ 每轮加 1。三个要素写在同一行,一眼看清循环的「起止步长」——这就是 for 比 while 更适合次数已知场景的原因。

逗号运算符在 for 里很常见——同时初始化或更新两个变量:

for (i = 0, j = 10; i < j; i++, j--) {
    printf("i=%d j=%d\n", i, j);
}

do-while:至少执行一次的出口条件循环

和 while 的最大区别:先干活,再问要不要继续。

while vs do-while 对比while — 入口条件① 先判断② 再执行体条件假 → 体一次都不跑do-while — 出口条件① 先执行体② 再判断真 → 再来一轮至少执行一次循环体③ 选型口诀可能一次都不做 → while;至少要跑一轮(菜单、输入校验)→ do-while
while 先检查条件再执行体;do-while 先执行体再检查条件——因此 do-while 保证循环体至少运行一次。

语法注意末尾的分号——while (条件); 这里的分号是 do-while 语法的一部分,不能省:

do {
    // 循环体——至少执行一次
} while (条件);   // 分号必须有!

菜单程序是经典用例——总得先显示选项,再让用户选:

#include <stdio.h>
int main(void) {
    int choice;
 
    do {
        printf("1. 新建  2. 打开  0. 退出\n");
        printf("请选择:");
        scanf("%d", &choice);
        /* 根据 choice 处理 ... */
    } while (choice != 0);
 
    return 0;
}

即使用户第一次就输入 0,菜单也会显示一次——这就是 do-while 和 while 的分水岭。

嵌套循环:外圈换行、内圈扫列

就是“循环里套循环”。矩形遍历常由外层控制行、内层控制列,三角形边界则让每行迭代次数不同:

打印九九乘法表——外循环控制被乘数、内循环控制乘数:

#include <stdio.h>
int main(void) {
    int i, j;
 
    for (i = 1; i <= 9; i++) {
        for (j = 1; j <= i; j++) {
            printf("%d×%d=%2d  ", j, i, i * j);
        }
        printf("\n");   // 每行结束换行——外循环的职责
    }
 
    return 0;
}

内循环的上界写成 j <= i 而不是固定的 9,所以每行只打印到对角线——三角形乘法表。如果把内循环改成 j <= 9,就是完整的 9×9 方阵。

代码详解:四种循环场景逐段拆解

场景一:for 计数循环——累加 1 到 N

#include <stdio.h>
int main(void) {
    int n, sum = 0;
 
    printf("N = ");
    if (scanf("%d", &n) != 1 || n < 0 || n > 65535) return 1;
 
    for (int i = 1; i <= n; i++) {
        sum += i;
    }
 
    printf("1+...+%d = %d\n", n, sum);
    return 0;
}

C99 起可以在 for 的初始化里声明变量(int i)——i 的作用域只在 for 循环内,出了循环就不能再用 i。sum += i 每轮把当前 i 累加进去,循环结束后 sum 就是等差数列求和。

场景二:while 哨兵值——统计正数个数

#include <stdio.h>
int main(void) {
    int x, count = 0;
 
    printf("输入整数(0 结束):\n");
    if (scanf("%d", &x) != 1) return 1;
 
    while (x != 0) {
        if (x > 0)
            count++;
        if (scanf("%d", &x) != 1) return 1;
    }
 
    printf("正数个数:%d\n", count);
    return 0;
}

哨兵这里是 0。循环条件 x != 0 和「0 表示结束」的约定必须一致——换哨兵值时,判断条件也要一起改。

场景三:do-while 输入校验——至少问一次

#include <stdio.h>
int main(void) {
    int age;
 
    do {
        printf("请输入年龄(1-120):");
        if (scanf("%d", &age) != 1) return 1;
    } while (age < 1 || age > 120);
 
    printf("年龄 %d 已记录\n", age);
    return 0;
}

如果用户第一次就输入合法年龄,循环也只跑一轮就退出。如果输入非法,会反复提示——用 while 也能写,但得把提示和读入拆到循环外,结构不如 do-while 直观。

场景四:嵌套循环——遍历 3×3 矩阵求和

#include <stdio.h>
int main(void) {
    int matrix[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    int row, col, total = 0;
 
    for (row = 0; row < 3; row++) {
        for (col = 0; col < 3; col++) {
            total += matrix[row][col];
        }
    }
 
    printf("矩阵元素总和 = %d\n", total);  // 45
    return 0;
}

外循环 row 从 0 到 2,内循环 col 从 0 到 2——每个 (row, col) 恰好访问一次,共 9 次,与 NestedLoopDiagram 里的访问顺序一致。

容易踩的坑

小结

  • 三种循环:while(先判后做,可能零次)、for(三要素计数,次数已知时最清晰)、do-while(先做后判,至少一次)
  • 哨兵值模式适合输入个数未知的场景:约定结束标记,先读入再判断
  • 嵌套循环的总次数按每轮内层次数求和;只有固定矩形边界才能直接相乘
  • for 的 init/cond/update 分工明确;退出时控制变量往往比最后一轮循环体的值大 1(当条件是 < 时)
  • 铁律:循环关键字后不加多余分号;循环体永远写花括号;控制变量必须有明确的更新路径

练习

问题 1(改代码型) 下面程序想打印 1 到 5,但有两处错误。找出并修正。

#include <stdio.h>
int main(void) {
    int i;
    for (i = 1; i < 5; i++);
        printf("%d ", i);
    return 0;
}

问题 2(问答型) 读程序,写出输出。解释 do-while 为什么至少执行一次。

#include <stdio.h>
int main(void) {
    int n = 10;
    do {
        printf("%d ", n);
        n++;
    } while (n < 10);
    return 0;
}

问题 3(综合实现型) 用嵌套 for 循环打印如下 4 行图案(每行字符数递增):

*
**
***
****

要求:外循环控制行数(1 到 4),内循环控制每行星号个数。不能使用字符串常量数组。

名词解释

名词解释

本章出现的专业名词,用大白话再讲一遍。

while 循环

先检查条件、条件为真才执行循环体的入口条件循环。条件一开始就不成立时,循环体一次都不执行。适合「可能零次」的重复任务。详见本章「while 回顾」一节和 WhileLoopFlowDiagram。

哨兵值(sentinel value)

标记数据结束的特殊值(如 -1、0)。循环每次先读入数据,再判断是否为哨兵——不是就处理并继续读入,是就退出。适合事先不知道输入个数的场景。详见 SentinelLoopDiagram 和「while 回顾」一节。

for 循环

把初始化、条件、更新写进括号的计数循环:for(init; cond; update) { ... }。初始化只执行一次,然后重复「判断→循环体→更新」。次数已知时比 while 更紧凑。详见 ForLoopFlowDiagram 和 Stepper 分步图示。

循环控制变量(loop control variable)

推动循环向终止条件靠近的变量(如 i、n)。必须在每轮循环中被更新(通常在 for 的 update 段或循环体末尾),否则条件永远不变就会死循环。忘了更新是最常见的死循环原因。

do-while 循环

先执行循环体、再检查条件的出口条件循环。语法末尾的分号不能少。保证循环体至少运行一次——适合菜单显示、输入校验等必须先做一轮的场景。详见 DoWhileVsWhileDiagram。

嵌套循环(nested loop)

循环体内再包含循环。固定矩形边界的总次数可相乘;内层边界依赖外层时,应把每轮内层次数相加。

原版目录概念补充核对

以下条目补齐官方目录中容易被示例主线掩盖的概念。它们不重复罗列目录,而是明确每项概念的机制、适用边界和验收证据。

关系运算符:机制、边界与证据

在《C控制语句:循环》的官方单元 cpr-06 中,关系运算符连接本章第 2 组知识约束。学习时要同时说明它接受什么输入、改变什么状态、在何种边界失效;再以本章示例的编译诊断、固定输入输出或失败用例复核结论,不能只记术语名称。

函数返回值:机制、边界与证据

在《C控制语句:循环》的官方单元 cpr-06 中,函数返回值连接本章第 6 组知识约束。学习时要同时说明它接受什么输入、改变什么状态、在何种边界失效;再以本章示例的编译诊断、固定输入输出或失败用例复核结论,不能只记术语名称。

讨论

评论区加载中…