循环语句 while & for

罗大富 BigRich大约 19 分钟C/C++

经过前面的学习,我们已经拥有了 收纳盒(变量),也有了分类决策的工具(条件语句),在生活中我们会将相同的物体归类收纳到同一个收纳盒中,这时就需要我们就需要去重复的将相同的物体放入盒子中,这一按照相同物体放入同一箱子规则的重复动作就是 循环

在正式开始讲解循环之前呢我们需要先来学习两种再循环中经常用到的两类运算符:

  • 赋值运算符
  • 自增自减运算符

有了他们的辅助,我们学习循环时就更容易去理解掌握循环。

赋值运算符

C 语言提供了多种复合赋值运算符,它们结合了赋值和算术,使得代码更加简洁高效。

我们在学习变量时就已经接触了最简单的赋值 =,例如:

int x = 5;  // 将 5 赋值给变量 x

我们之前也已经学习过了常用的算术运算符,今天呢我们就来学习将赋值和算数相结合的复合的赋值运算符。我们常用的复合赋值运算符有以下五种:

运算符含义表达式等价形式
+=加法赋值a += ba = a + b
-=减法赋值a -= ba = a - b
*=乘法赋值a *= ba = a * b
/=除法赋值a /= ba = a / b
%=模运算(取余)赋值a %= ba = a % b

这 5 个运算符,分别表示 5 种不同的运算。这 5 个运算每个都是由两个符号组成,如 +=,两个符号组成一个整体表示一个运算,不能拆开,更不能随便在 += 中间加空格。

我们可以使用这 5 种赋值运算符来进行下面的练习:

#include <stdio.h>

int main() {
    int a = 5;
    int b = 3;

    a += b;   // a = a + b → a = 8
    printf("a = %d\n", a);

    b -= a;   // b = b - a → b = -5
    printf("b = %d\n", b);

    a *= 2;   // a = a * 2 → a = 16
    printf("a = %d\n", a);

    a /= 3;   // a = a / 2 → a = 5
    printf("a = %d\n", a);

    a %= 2;   // a = a % 2 → a = 1
    printf("a = %d\n", a);

    return 0;
}

我们在进行赋值操作的时候,需要注意:在赋值过程中,如果两侧类型不一致,会发生类型转换。比如:实型数据赋给整型变量时,舍弃实数的小数部分,示例如下:

// 定义一个 double 类型变量 d
double d = 72.33;
// 再定义一个 int 类型变量 i,并将 d 的值赋值 i
int i = d;     
// 由于 i 是整型,因此只保留 d 的整数部分
printf("i: %d\n", i);
// d 保持不变
printf("d: %.2f\n", d);  

自增自减运算符

自增(++)自减(--) 运算符同 +、-、*、/ 一样,++、-- 也是运算符,是 C 语言中常用的单目运算符,用于对变量进行加 1 或减 1 操作,自增自减运算符在循环结构中非常常见。虽然它们看似简单,但在实际使用中有许多需要注意的细节和潜在问题。

自增运算符(++)自减运算符(--) 是 C 语言中用于对变量进行简单加减操作的特殊运算它们的含义分别是使变量的值自增 1、自减 1,++、-- 都是两个字符整体作为一个符号,不能拆开,更不能理解为连加、连减。

++、-- 的优先级很高,仅次于括号 (),因此一般无括号时都应先算 ++、--

这两个运算符都有以下两种使用形式:

  1. 前置形式,例如:++i
  2. 后置形式,例如:i--

无论是前置还是后置,如果我们将自增自减运算的语句单独放在一行,i++++i 是一样,都是使 i 自增 1,i----i 也是一样的,都是使 i 自减 1。代码如下:

#include <stdio.h>

int main() {
    int i = 5;
    printf("初始的 i: %d\n", i);

    i++;    // 等同于 i += 1 / i = i + 1
    printf("i++ 之后的 i: %d\n", i);

    ++i;    // 等同于 i += 1 / i = i + 1
    printf("++i 后 i: %d\n", i);

    --i;    // 等同于 i -= 1 / i = i - 1
    printf("--i 后 i: %d\n", i);

    i--;    // 等同于 i -= 1 / i = i - 1
    printf("i-- 后 i: %d\n", i);

    return 0;
}

这也就说明,++-- 放在变量的前边或后边,单独写一行,结果是一样的。

但是,当自增自减运算符出现在表达式中,参与计算时,我们再来看看二者之间的区别:

#include <stdio.h>

int main() {
    int a = 5;
    printf("a: %d\n",a);

    int b = a++;
    printf("b: %d\n", b);

    int c = ++a;
    printf("c: %d", c);

    return 0;
}

前置形式

前置形式是指运算符位于变量之前:

  • ++i:先对 i 加 1,然后使用 i 的新值
  • --i:先对 i 减 1,然后使用 i 的新值

我们先来看看下面这段代码运行的结果:

int i = 5;
int j = ++i;                       // i先增加到6,然后j赋值为6
printf("i = %d, j = %d\n", i, j);  // 输出:i = 6, j = 6

通过上面代码运行后得到的结果可以看出:先将 i 的值增加到 6 ,再把 i 的新值赋值给了 j。

接着,我们再来看看 --i 的示例:

int i = 5;
int j = --i;                        // i 先减少到 4,然后 j 赋值为 4
printf("i = %d, j = %d\n", i , j);  // 输出:i = 4, j = 4

通过上面代码运行后得到的结果可以看出:先将 i 的值减少到 4 ,再把 i 的新值赋值给了 j。

了解了基本用法之后我们来看一个综合案例:

#include <stdio.h>

int main() {
    int i = 10;
    int j = --i;
    int k = ++i;
    printf("i = %d, j = %d, k = %d\n", i , j, k);

    return 0;
}

首先 i 的初始值为 10,先使用了 --i, 此时 i 先进行了一次自减新的值为 9,之后将新的值 9 赋值给了 j。之后使用了 ++i,但是注意在使用 ++i 之前已经进行了一次 --i 的运算所以当前进行自增运算前的 i 的值是 9 不是初始值 10,大家一定要注意这个点千万不要犯迷糊,所以进行自增运算之后 i 的值变又变回到了 10,此时又将新的值 10 赋值给了 k ,所以最后的结果是:

i = 10, j = 9, k = 10

后置形式

后置形式是指运算符位于变量之后:

  • i++:先使用 i 的当前值,然后对 i 加 1
  • i--:先使用 i 的当前值,然后对 i 减 1

i++ 示例:

int i = 5;
int j = i++;                       // j 先赋值为5,然后 i 增加到6
printf("i = %d, j = %d\n", i, j);  // 输出:i = 6, j = 5

通过上面的代码我们可以看出与前置形式的 ++i 不同的是 i++ 先进行了赋值操作,将原始值先赋值给了 j,之后在进行自增运算后将自身的值变为了 6。

i-- 示例:

int i = 5;
int j = i--;                       // j 先赋值为5,然后 i 减少到4
printf("i = %d, j = %d\n", i, j);  // 输出:i = 4, j = 5

同理我们可以看出与前置形式的 --i 不同的是 i-- 先进行了赋值操作,将原始值先赋值给了 j,之后在进行自减运算后将自身的值变为了 4。

同样的我们来看一个综合案例:

#include <stdio.h>

int main() {
    int i = 10;
    int j = i--;
    int k = i++;
    printf("i = %d, j = %d, k = %d\n", i, j, k);

    return 0;
}

同样的大家先自己捋一下思路,看看最后的值分别是什么。首先 i 的初始值为 10,使用了i--, 与 --i 不同的是此时 i 先进行了赋值,现在的初始值 10 赋值给了 j,之后才进行自减匀运算,经自身数值更新为 9。之后同样与 ++i 不同的是,先将现在的值 9 赋值给了 k 之后进行自增运算将自身的值更新为 10,此时 i 的值又变回了10。所以最后的结果是:

i = 10, j = 10, k = 9

我们现在就学习完了所有的自增自减运算符,通过之前几个例子的对比,我们可以很清晰的看到运算符位于左、右就有区别了,所以在表达式中使用时一定要考虑清楚之后再使用。

有了赋值运算符和自增自减运算符的铺垫,接下来让我们正式进入循环的学习。

while 语句

在什么情况下,我们需要用到循环?先举一个例子,在控制台中按顺序打印 1~10,代码如下:

int i = 1;
printf("%d ", i++);
printf("%d ", i++);
printf("%d ", i++);
printf("%d ", i++);
printf("%d ", i++);
printf("%d ", i++);
printf("%d ", i++);
printf("%d ", i++);
printf("%d ", i++);
printf("%d ", i++);

那如果,我们要打印 1 - 1000 或者 1 - 10000 呢?因此,这样写肯定是不合理的。计算机最擅长的事情就是可以反复地做类似的、重复的工作而不觉得厌烦,而且它凭借着极快的运行速度,保证在很短的时间内完成,而实现这种工作的方式就是 循环

在 C 语言中,循环可以将重复的部分在程序中只写一次,而让计算机反复地执行多次。循环结构有三种:

  1. while 循环
  2. do-while 循环
  3. for 循环。

while 语句的写法与 if 类似,只是把关键字由 if 换成 while,语法如下:

while (条件表达式) {  
    //  循环体(条件成立时,会被反复执行的代码)
    // 有限循环中还包含条件控制语句
} 

while 循环的执行流程:

其逻辑是:如果 条件表达式 成立(即为真),就执行一次 循环体语句,并且循环后变量自增一,再次回到条件判断语句,若还为真就继续进行循环,直到条件判断语句不满足即为假时,则跳出循环执行循环以外的其他语句。

例如,我们打印数字 1 到 10,由于打印数字 1 - 10 是:

#include <stdio.h>


int main(){
    // 初始化变量
    int i = 1;

    // 循环条件:i 小于等于 10 时继续循环
    while(i <= 10){
        // 依次打印 1 到 10 的值
        printf("%d ", i);
        // 每次循环后 i 自增 1,该循环的条件控制语句
        // 没有 i++ 会导致死循环
        i++;
    }
    return 0;
}

如果没有条件控制语句,那么我们的 while 循环就会变成无限循环,例如:

#include <stdio.h>


int main(){

    while(1){
        printf("感谢关注罗大富,一键三连加关注!");
    }
    return 0;
}

由于此处的条件表达式为 1,并且是常量,无法被改变,也就是说该程序会一直持续运行下去,永远不会结束,这样的无线循环,因为条件永远为真,也被称为 永真循环

接着,我们再来看 2 个练习:

练习 1:给定一个正整数 n,计算并输出 1 + 2 + 3 + ... + n 的和,代码如下:

#include <stdio.h>


int main() {
    // 给定正整数 n
    int n = 10;
    // 初始化求和的变量
    int sum = 0;

    // 定义循环变量 i
    int i = 1;
    while (i < n) {
        sum += i;
        i++;
    }

    printf("1 加到 %d 的和为:%d", n, sum);
    return 0;
}

练习 2:找出所有 3 位水仙花数(各位数字立方和等于该数本身,如 153 = 1³ + 5³ + 3³),代码如下:

#include <stdio.h>


int main() {
    // 定义循环变量
    int i = 100;
    while (i <= 999) {
        // 百位 345 / 100 = 3
        int a = i / 100;
        // 十位   345 / 10 = 34 % 10 = 4
        int b = i / 10 % 10;
        // 个位  345 % 10 = 5
        int c = i % 10;

        // 判断是否为水仙花数
        if (a*a*a + b*b*b + c*c*c == i) {
            printf("%d ", i);
        }

        // 更新循环变量
        i++;
    }
}

while 循环的核心是先判断条件,再执行循环体,适用于循环次数未知,但知道明确的结束条件的场景,如输入验证、文件读取。其特点为灵活性高,可能零次执行。

do-while 循环

学习完 while 循环之后,我们再来学习 do-while 循环,do-whilewhile 类似,形式如下:

do {  
    // 循环体  
} while (条件表达式);

while 循环不同的是,while 循环是先判断条件,再执行循环体;而 do-while 循环则是先无条件执行一次循环体,再判断条件表达式是否为真。若为真则继续循环,否则退出。流程图如下:

我们也可以把 while 循环的两个练习题换成使用 do-while 重写一遍。

练习 1:给定一个正整数 n,计算并输出 1 + 2 + 3 + ... + n 的和,代码如下:

#include <stdio.h>


int main() {
    // 初始化变量 n 与 sum
    int n = 10, sum = 0;

    // 循环变量
    int i = 1;
    do {
        sum += i;
        i++;
    } while (i <= n);

    printf("1 + ... + %d = %d", n, sum);
}

练习 2:找出所有 3 位水仙花数(各位数字立方和等于该数本身,如 153 = 1³ + 5³ + 3³),代码如下:

#include <stdio.h>


int main() {
    // 循环变量
    int i = 100;
    do {
        // 获取个位十位百位数字
        int a = i / 100;
        int b = i / 10 % 10;
        int c = i % 10;

        if (a*a*a + b*b*b + c*c*c == i) {
            printf("%d ", i);
        }
        i++;
    } while (i <= 999);

}

do-while 循环的核心是 先无条件执行一次循环体,再判断条件是否成立,适用于必须执行至少一次循环体,再根据条件决定是否继续的场景,如初始化硬件、加载配置文件、显示交互式菜单。其特点为避免在循环外重复初始化代码,逻辑更简洁。

for 循环

最后一个循环就是 for 循环,相比于前两个而言,for 循环的使用频率最高的,基本语法如下:

for(初始化; 条件表达式; 条件控制语句) {
    // 循环体
}

for 循环的优点就是初始化、条件判断、更新操作都在一行,清清楚楚。我们不需要在循环的外部单独定义一个循环变量(初始化语句),也不需要在循环体中添加条件控制语句(i++、i--),所有操作都只需要写在 for 语句后面的小括号中,不同部分用分号 ; 隔开

流程图如下:

例如,我们可以使用 for 循环在控制台中打印数字 1-10,代码如下:

#include <stdio.h>


int main() {
    for (int i = 1; i <= 10; i++) {
        printf("%d ", i);
    }
}

相对于 while 循环的写法,for 循环仅用三行就可以实现相同的功能。

接着,我们再来将之前的两个练习用 for 循环重写。

练习 1:给定一个正整数 n,计算并输出 1 + 2 + 3 + ... + n 的和,代码如下:

#include <stdio.h>


int main() {
    int n = 10, sum = 0;

    for (int i = 1; i <= n; i++) {
        sum += i;
    }

    printf("1 + ... + %d = %d", n, sum);
}

练习 2:找出所有 3 位水仙花数(各位数字立方和等于该数本身,如 153 = 1³ + 5³ + 3³),代码如下:

#include <stdio.h>


int main() {
    for (int i = 100; i <= 999; i++) {
        int a = i / 100;
        int b = i / 10 % 10;
        int c = i % 10;

        // 判断是否为水仙花数
        if (a*a*a + b*b*b + c*c*c == i) {
            printf("%d ", i);
        }
    }

}

循环嵌套

所谓嵌套循环就是在一个循环体内包含另一个或多个完整的循环结构。内循环或外循环可以是任何类型,例如 while 循环或 for 循环。 结合我们刚才学习过的三种循环,可以组合成多种循环嵌套如 for 循环嵌套、while 循环嵌套、do-while 循环嵌套和多种混合循环嵌套。

我们先用一个 for 循环嵌套的一般形式为例,语法结构如下:

for (初始化; 条件表达式; 条件控制语句){     
    //  外层循环体
    
    for(初始化; 条件表达式; 条件控制语句){
        //  内层循环体
    }
    
    // 外层循环体
}

其核心规则是:外层循环每执行一次,内层循环需完整执行一轮。

  • 外层循环单次触发:外层循环每执行一次迭代(如 i++),内层循环必须完整执行全部迭代(如 j 从初始值到终止条件)。
  • 内层循环彻底遍历:内层循环相当于外层循环的“子任务”,必须完成自身所有逻辑后才能回到外层循环继续执行。

下面是一个简单的循环嵌套的示例:

#include <stdio.h>


int main() {
    for (int i = 0; i < 3; i++) {        // 外层循环(行)
        for (int j = 0; j < 4; j++) {     // 内层循环(列)
            printf("i: %d,j: %d;    ", i, j);
        }
        printf("\n");
    }
}

执行流程:

  • i = 0时,内层 j 遍历 0 → 1 → 2 → 3,完整输出第一行所有列;
  • i = 1 时,内层 j 再次从 0 → 3,输出第二行;
  • i = 2 时,j 依旧从 0 → 3,输出第三行;

外层仅触发 3 次,内层总共执行了 3 x 4 = 12 次。

注意

内外两层循环的循环变量必须使用不同的变量名,若同名会导致逻辑错误。

我们已经知道了什么是嵌套循环,那么我们学习两个案例。

练习 1:输出一个 5 行直角三角形,要求每行星号数量递增:

*
**
***
****
*****

代码如下:

#include <stdio.h>


int main() {
    // 外层循环:控制打印的行数(从第1行到第5行)
    for (int i = 1; i <= 5; i++) {  // i表示当前行号(1~5)

        // 内层循环:控制每行打印的星号数量(第 i 行打印i个星号)
        for (int j = 1; j <= i; j++) {  // j 表示当前行的第j个星号
            printf("*");  // 打印一个星号(不换行)
        }

        printf("\n");  // 每行星号打印完成后换行(切换到下一行)
    }

    return 0;
}

练习 2:打印九九乘法表,代码如下:

#include <stdio.h>


int main() {
    // 外层循环控制行数(1~9行)
    for(int i=1;i<=9;i++){     // i表示当前行(被乘数)
        // 内层循环控制当前行的列数(每行i个算式)
        for(int j=1;j<=i;j++){ // j表示乘数
            // 使用格式控制符排版保证输出结果美观:
            // %-2d是为了保证等号后结果占2字符宽度,对齐美观
            printf("%dx%d=%-2d ",j,i,i*j); 
        }
        // 每行输出完毕后换行
        printf("\n");
    }
    
    return 0;
}

最后还有一点需要注意的是:嵌套循环复杂度随层级指数增长,建议不超过 3 层,否则可读性与性能均下降。

break 与 continue

一般来说,程序在进入循环之后,需要先执行完循环的所有内容,但是,在有些时候我们不想让循环一直进行下去,而是在遇到某些特殊情况的时候将循环给终止或者跳过当前的循环,那么我们该怎么去实现这一操作呢?这时候就需要用到我们的跳转控制语句。

C 语言循环中的跳转控制语句主要用于在循环执行流程中改变代码的执行顺序,实现流程的动态转向,简单点来说就是在循环过程中,跳转到其他语句执行。跳转控制语句其实就是两个关键字:breakcontinue,接下来我们展开说说这两个关键字。

break 的功能是:强制终止当前循环,直接跳出最近的循环体或分支,继续执行后续代码。那么让我们通过一个示例来说明如何去使用 break

#include <stdio.h>

int main() {
    // for 循环初始化:i 从 0 开始,每次循环后 i 自增 1,循环条件 i<10
    for (int i = 0; i < 10; i++) {
        // 条件判断:当 i 等于 4 时执行 break
        if(i == 4) {
            // 强制终止整个 for 循环,后续循环不再执行
            break;  
        }
        // 输出当前 i 的值(注意空格分隔)
        printf("%d ", i);  
    }
    
    return 0;
}

这段程序实现了一个 break 的循环终止:当变量 i 的值为 4 时循环不再进行,此时输出的只有前四次循环的值 0 1 2 3,之后的数由于强制被中断所以不在打印输出。

接着,我们来看一道练习题:

判断给定的正整数 n 是否为素数(质数)。

质数(英文名:Prime number)又称素数,是指在大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的自然数。

循环遍历,检查是否存在除了 1 和 n 以外,还能被整除的其他数,存在则立即终止检查(使用 break)。

代码如下:

#include <stdio.h>


int main() {
    int n = 7, is_prime = 1;

    for (int i = 2; i < n; i++) {
        if (n % i == 0) {
            is_prime = 0;
            break; // 发现因子后立即终止循环
        }
    }

    if (is_prime) {
        printf("%d 是质数", n);
    } else {
        printf("%d 不是质数", n);
    }
    return 0;
}

continue 的功能是:跳过当前循环的剩余代码,直接进入下一次循环的条件判断。那么在上一个示例中我们仅仅只是想要跳过 4 这一个数字,而其他数字都进行保留,这时候我们只需要将 break 替换为 continue 即可实现:

#include <stdio.h>


int main() {
    // for 循环:i 从 0 开始,每次循环递增 1,直到 i<10 不再满足
    for (int i = 0; i < 10; i++) {
        // 检查 i 是否等于 4
        if(i == 4) {
            continue;  // 跳过本次循环的后续代码,直接进入下一次循环迭代
        }
        printf("%d ", i);  // 打印当前 i 的值(空格分隔)
    }
    
    return 0;
}

break 不同的是:当满足条件执行 continue 时,仅仅只是跳过了本次的循环,直接跳到了下一次的循环,这样我们就可以淘汰掉我们不想要的那一个数字,实现我们要达到的目的。

我们来看一道练习题:

跳过奇数,累加偶数

使用 for 循环遍历 1~n,跳过所有奇数,仅累加偶数并输出结果。

代码如下:

#include <stdio.h>

int main() {
    int n = 10, sum = 0;
    for (int i = 1; i <= n; i++) {
        if (i % 2 != 0) {
            continue; // 跳过奇数
        }
        sum += i;
    }
    printf("1~%d 的偶数和为:%d", n, sum);
    return 0;
}

注意

在嵌套循环中,breakcontinue 只终止当前层的循环,外层循环不受影响;

breakcontinue 只在循环(for、while、do-while)与 switch 中生效,不能单独使用。

最后,我们再来看两道嵌套循环与跳转控制语句的综合性练习题目:

练习 1:使用嵌套循环与 break:

使用嵌套循环打印一个 5x5 的矩阵,满足以下规则:

主对角线(从左上到右下)上的数字为 1,其他位置为 0。

但当行号和列号之和超过 5 时,立即停止该行的后续列打印(用 break)。

示例输出:

10000
0100
001
00
0

代码如下:

#include <stdio.h>

int main() {
    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 5; ++j) {
            if (i + j >= 5) {
                break;
            }
            if (i == j) {
                printf("1");
            } else {
                printf("0");
            }
        }
        printf("\n");
    }
    return 0;
}
上次编辑于:
贡献者: 罗大富BigRich