C语言:循环结构
什么是循环结构
代码在满足某种条件的前提下,重复执行,就叫做循环结构。
循环的分类
无限循环:其实就是死循环,程序设计中要谨慎使用。
有限循环:循环限定循环次数(for)或者终止循环的条件(while,do..while)。
循环的构成
循环条件: 如循环次数或者循环的出口。
循环体:需要重复执行的代码(也就是{}包裹的内容)
当型循环的实现
特点:先判断,后执行,如果条件不满足,循环体一次都不执行。
代表:while、for
while循环
语法:
【1】循环变量;while (【2】循环条件){【3】循环语句;【4】更新循环变量;}// 注意:如果循环语句是单语句,可以省略{}while (循环条件) 循环单语句;while (循环条件)循环单语句;// 注意:有些特殊场景下,循环体语句糅合到了循环条件中,所以省略掉循环体while (循环条件); // int i = 1; while(i++ <= 5); printf("%d\n",i);// 7
说明:
循环条件的返回值必须是逻辑值(使用
非0
表示真,使用0
表示假,C语言底层使用1
表示真)C99版本引入了一个stdbool.h
,可以使用true或者false来表示真假,这里的true和false实际上就是定义的两个符号常量,true的值是1,false的值0。{}
包起来的内容整体称之为循环体。我们要在循环体中控制循环条件的变化,也可以在循环条件中控制循环条件的变化,否则产生死循环。
执行过程:
特点: 先判断,后执行,循环体语句有可能一次都不执行。
案例
需求:求1~100的累加和(例如:1+2+3+4+..+100)
分析:
创建一个变量sum = 0,用来接收累加和
创建一个循环变量i,用来表示计算数,给一个默认值1,每次循环的时候 i++
在循环中,使用 sum += i(sum = sum + i),完成累加和运算
同时我们要限定循环的条件,也就是i的范围:i <= 100
第1次:sum += i = 0 + 1 = 1
第2次:sum += i = 1 + 2 = 3
第3次:sum += i = 3 + 3 = 6
...
代码:
/*************************************************************************> File Name: demo01.c> Author: 三道渊> Description: > Created Time: 2025年07月15日 星期二 10时42分47秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[]){// 需求:求1~100的累加和// 创建一个变量sum = 0,用来存储累加和,因为参与计算,所以必须有初始值int sum = 0;// 创建一个 循环变量并初始化int i = 1;// 通过一个循环生成1~100的自然数,并实现累加和运算while(i <= 100) // ()循环条件,限制循环变量的变化,循环的出口限制{// 累加和运算sum += i;// 更新循环变量,逼近出口i++;// 等价于 i = i + 1 i+=1 ++i}printf("1~100的累加和是%d\n",sum);return 0;}
运行结果:
案例
需求:求1~100以内的偶数和
分析:
创建一个变量sum = 0,用来存储累加的偶数和
创建一个循环变量i = 2,存储参与运算的自然数(最小的偶数是2)
创建一个循环,设置循环条件 i <= 100
在循环体内,需要用一个if校验i是否是偶数,如果满足条件,就实现偶数和的计算:sum += i
在循环体的最后一行,需要更新循环变量,作用是逐渐逼近循环的出口
循环结束后,才能输出sum。
代码:
/*************************************************************************> File Name: demo02.c> Author: 三道渊> Description: > Created Time: 2025年07月15日 星期二 11时02分40秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[]){// 创建一个变量sum = 0,用来存储累加的偶数和int sum = 0;// 创建一个循环变量i = 2,存储参与运算的自然数(最小的偶数是2)int i = 2;// 创建一个循环,设置循环条件 i <= 100while (i <= 100){// 在循环体内,需要用一个if校验i是否是偶数,如果满足条件,就实现偶数和的计算:sum += iif (i % 2 == 0)sum += i;// 在循环体的最后一行,需要更新循环变量,作用是逐渐逼近循环的出口i++;// if (i % 2 == 0) sum += i; i++; // 等价于上面代码}// 循环结束后,才能输出sum。printf("1~100以内的偶数和是%d\n", sum);return 0;}
运行结果:
案例
需求:通过键盘录入一个整数,判断这个整数是否是水仙花数。
分析:
拆分一个n位的数,比如拆分为如:个位、十位、百位...
要求这个n位数的每一位的n次幂相加等于这个数本身:
比如一个三位数:
个位的3次幂 + 十位的3次幂 + 百位的3次幂 = 这个数本身
代码:
/*************************************************************************> File Name: demo06.c> Author: 三道渊> Description: > Created Time: 2025年07月15日 星期二 14时32分55秒************************************************************************/#include <stdio.h>#include <math.h>int main(int argc,char *argv[]){int num, originalNum, n = 0, sum = 0;// 从控制台录入一个数printf("请输入一个整数:");scanf("%d", &num);// 用一个变量记录这个整数originalNum = num;// 计算数字的位数while (num != 0){num /= 10;n++;// 统计位数}// 恢复原始数据num = originalNum;// 计算各位数的n次幂之和while (num != 0){// 将需要的位数拆出来int digit = num % 10;// 计算这个数的n次幂,并相加sum += pow(digit,n);// pow(运算数,运算数的n次方)// 将需要拆除的位保留在个位num /= 10;}// 校验并输出if (sum == originalNum)printf("%d 是水仙花数。\n", originalNum);elseprintf("%d 不是水仙花数。\n", originalNum);return 0;}
编译命令:
gcc demo06.c -lm // math.h中有些函数属于GNU扩展函数,不是C语言标准,需要指定链接
解释:
-lm
意思是链接数学库,数学库中有一些函数不是C语言标准定义的,属于其他扩展库,C语言标准库是自动链接的,但是扩展库不会自动链接,需要我们指定链接。运行效果:
while的特殊写法:
while(1) // 死循环
for循环
原则上:for循环能实现的,while循环也可以实现,for循环可以看做是while循环的一种特殊写法,for循环胜在结构清晰,非常适合于直到循环次数的循环。
语法:
for (【1】循环变量; 【2】循环条件; 【4】更新循环变量){【3】循环语句;}// 如果循环体语句是单语句,也是可以省略{}for (【1】循环变量; 【2】循环条件; 【4】更新循环变量)【3】循环语句;for (【1】循环变量; 【2】循环条件; 【4】更新循环变量)【3】循环语句;
说明:
① ()
中可以仅保留两个;;
,举例:for (;;)
,此时这种写法就是死循环的一种体现。
② 循环变量
:需要赋初始值,循环变量可以是单个,也可以是列表,多个循环变量之间使用逗号分隔,举例:
// 循环变量是单个for (int i = 0;..;..) {..}// 循环变量是多个for (int i = 0,j = len -1;..;..){..}
③ 循环条件
:用来限制循环的次数,循环条件支持任意表达式(常用的有关系表达式,逻辑表达式),举例:
for (..;i < 10 && j >= 0;..){..}
④:更新循环变量
:支持列表,列表使用逗号分隔,这里可以使用赋值表达式,举例:
for (..;..;i++,j--){..}
⑤ 执行顺序:①②③④ → ②③④ → ②③④ → ... → ②
执行过程:
特点:先判断,后执行,循环体语句有可能一次都不执行。
案例
需求:计算1~100以内的偶数和
代码:
/*************************************************************************> File Name: demo03.c> Author: 三道渊> Description: > Created Time: 2025年07月15日 星期二 11时34分44秒************************************************************************/#include <stdio.h>/*** 使用while实现*/ int while_test(){int sum = 0;// 循环变量(需要初始化)int i = 2;// 循环条件while (i <= 100){// 循环语句if (i % 2 == 0) sum += i;// 更新循环变量i++;}// 打印输出printf("1~100以内的偶数和是%d\n", sum);return 0;}/*** 使用for实现(不推荐)*/ int for_test(){int sum = 0;// 循环变量(需要初始化)int i = 2;// 循环条件for (;i <= 100;){// 循环语句if (i % 2 == 0) sum += i;// 更新循环变量i++;}// 打印输出printf("1~100以内的偶数和是%d\n", sum);return 0;}/*** 使用for实现(推荐)*/ int for_test2(){int sum = 0;// 循环条件for (int i = 2; i <= 100; i++){// 循环语句if (i % 2 == 0) sum += i;}// 打印输出printf("1~100以内的偶数和是%d\n", sum);return 0;}int main(int argc,char *argv[]){while_test();for_test();for_test2();return 0;}
案例
需求:用户通过键盘输入一个整数n,求n的阶乘。例如:n = 4,n的阶乘 1 * 2 * 3 * 4
代码:
/*************************************************************************> File Name: demo04.c> Author: 三道渊> Description: > Created Time: 2025年07月15日 星期二 11时45分48秒************************************************************************/#include <stdio.h>// 数学库对应的头文件#include <math.h>int main(int argc,char *argv[]){// 创建一个变量,用来存储计算数unsigned long n;// 创建一个变量,用来存储乘积 size_t 是C语言中 unsigned long的别名size_t r = 1;// 通过控制台,给n赋值printf("请输入一个整数:\n");scanf("%lu", &n);// 通过for循环计算for (int i = 1; i <= fabs(n); i++) r *= i;printf("1~%lu的乘积结果是%lu\n",(size_t)fabs(n), r);return 0;}
注意:阶乘计算,如果乘积超过变量存储的范围,会溢出,结果计算可能不准确。
for实现死循环:
for(表达式1;;表达式3) {..}for(表达式1;;) {..}for(;;表达式3) {..}for(;;) {}for(表达式1;表达式2;) {}...
循环实现的三要素
循环变量的初始化,举例:int i = 2
循环条件,举例:i <= 100
循环变量的更新,举例:i++
案例
需求:求斐波拉契数列前20个
分析:
斐波拉契数列指的是是符合一定规则的数列,举例:1,1,2,3,5,8...
斐波拉契数列的特点:第3个数等于前两个数之和,最开始的第1,2个数是固定的,都是1
代码:
/*************************************************************************> File Name: demo05.c> Author: 三道渊> Description: > Created Time: 2025年07月15日 星期二 14时13分31秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[]){// 创建一个变量,存储第1,2个数,默认都是1int f1 = 1, f2 = 1;// 定义循环变量int i = 1; // 每一次循环产生2个数,也就是循环10次// 通过循环产生数列for (; i <= 10; i++){// 输出当前两个数列printf("%-12d%-12d\n",f1,f2);// 1,1 // 计算得到新的f1,f2f1 += f2; // f1 = f1 + f2; // 第1次:f1 = 1+1 = 2 ..f2 += f1; // f2 = f2 + f1; // 第1次:f2 = 1 + 2 = 3 ..}return 0;}
运行结果:
直到型循环的实现
特点:先执行,后判断,不管条件是否满足,至少执行一次。
代表:do..while
do..while循环
语法:
【1】循环变量;do{【2】循环体语句;【3】更新循环变量;} while(【4】循环条件);
说明:
① 循环条件
的返回值必须是逻辑值
(0和非0(计算机返回1))
② {}
包起来的内容称之为循环体
③ 我们要在循环体
中控制循环条件
的变化,否则会产生死循环。
执行过程:
特点:先执行,后判断,不管条件是否满足,循环体语句至少执行一次。
案例
需求:求1~100 以内的偶数和
分析:
创建一个变量,用来存储偶数和sum = 0
创建一个循环变量,i = 2
创建一个do..while循环,在循环体中,校验 i % 2 == 0,如果满足,就实现偶数和计算 sum += i
计算完成,在do..while循环体的末行,更新循环变量,使其逼近出口
限制性循环的出口:i <= 100 (i > 100 ,循环结束)
循环结束,打印输出sum的值
代码:
/*************************************************************************> File Name: demo07.c> Author: 三道渊> Description: > Created Time: 2025年07月15日 星期二 16时29分23秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[]){// 创建一个变量,用来存储偶数和sum = 0 int sum = 0;// 创建一个循环变量,i = 2int i = 2;// 创建一个do..while循环,在循环体中,校验 i % 2 == 0,如果满足,就实现偶数和计算 sum += ido{if (i % 2 == 0) sum += i;// 计算完成,在do..while循环体的末行,更新循环变量,使其逼近出口i++;} while(i <= 100);// 限制性循环的出口:i <= 100 (i > 100 ,循环结束)// 循环结束,打印输出sum的值printf("1~100以内的偶数和是%d\n", sum);return 0;}
案例
需求:用C语言编写简单猜数字游戏代码。游戏规则是程序随机生成一个1到100之间的数字,玩家通过输入猜测数字,程序会提示猜测是太大还是太小,直到玩家猜中为止。
代码:
/*************************************************************************> File Name: demo08.c> Author: 三道渊> Description: 猜数字> Created Time: 2025年07月15日 星期二 16时37分16秒************************************************************************/#include <stdio.h>#include <stdlib.h>#include <time.h>int main(int argc,char *argv[]){// 创建变量number(随机数),guess(用户猜测的数字),count(计数)int number, guess, count = 0;// 设置随机种子srand(time(NULL));// 生成随机数 1~10之间的随机数 rand() % 最大范围整数,生成的实际数的范围 0 ~ 最大范围整数 - 1number = rand() % 10 + 1;// 设计头printf("----------------------\n");printf("| 猜数字小游戏v1.0 |\n");printf("----------------------\n\n");printf("我已经想了一个1~10之间的整数,你猜猜是多少?\n");// 猜数字,猜不对,就一直猜,猜到对为止do{printf("请输入你的猜测:\n");// 接收scanf的返回值int result = scanf("%d", &guess);// 对非法输入进行校验(比如:我们要求输入数值,但是不小心输入了非数值,此时就是非法输入)if (result != 1){// 如果输入的不是数字,就是非法的,也就是输入的内容类型跟scanf提供的格式化符号不匹配// 每次重新输入前,需要清空输入缓冲区,重新从外部设备获取输入到缓冲区while (getchar() != '\n'); // getchar() 类似于 scanf("%c",c);printf("请输入数字!\n");continue; // 这次循环,跳过不算}// 计算count ++;// 校验if (guess > number) printf("太大了,再猜一次!\n");else if (guess < number) printf("太小了,再猜一次!\n");else {printf("恭喜你,猜对了!\n");printf("你一共猜了%d次!\n", count);}} while(guess != number); // 等价于 guess > number || guess < numberreturn 0;}
运行效果:
goto(了解)
语法:
goto 标签(label)
标签:标明目标代码的位置,是一个不加""
的字符串。
案例:
需求:求1~100以内的偶数和
代码:
/*************************************************************************> File Name: demo08.c> Author: 三道渊> Description: > Created Time: 2025年07月15日 星期二 17时10分10秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[]){// 创建一个变量,用来存放偶数和int sum = 0;// 创建一个循环变量int i = 2;// 定义goto标签,名字自己取,符合标识符命名即可loop:// 过滤偶数if (i % 2 == 0) sum += i; // 偶数和计算i++; // 更新循环变量// 循环条件if (i <= 100) goto loop; // 标签loop由goto触发printf("1~100以内的偶数和是%d\n",sum);return 0;}
注意事项
可读性:
goto
语句会破坏代码的结构和可读性,使得代码难以理解和维护。因此,应尽量避免使用。替代方案:大多数情况下,可以使用循环、条件语句、函数等结构来替代
goto
语句,使代码更加清晰和易于管理。嵌套限制:虽然
goto
语句可以跨函数跳转(即跳转到另一个函数中的标签),但这种用法是不合法的,并且会导致编译错误。goto
语句只能在同一函数内部跳转。错误处理:在某些情况下,
goto
语句可以用于错误处理,例如从嵌套的多层循环中跳出。但即使在这种情况下,也应谨慎使用,并考虑是否有更好的替代方案。
总结
虽然
goto
语句在C语言中是合法的,并且有时可能看起来很方便,但过度使用或不当使用会导致代码质量下降。因此,建议尽量避免使用goto
语句,而是采用更结构化和可维护的编程方法。
循环结构的典型应用场景
求累和:举例:
1+2+3..+100的和,sum = 0
求累积:举例:
1*2*3..*100的积,result = 1
求均值:举例:
(1+2+3...+100)/100的值
求极值:举例:
12,34,56,67中的最大值、最小值
元素遍历:常用于数组元素的遍历。
...
基础算法模型
累和
定义一个变量(sum),并赋初值为0;
该变量累加(+=)每一个数据项(i);
当访问完每一个数据项,此时该变量的取值就是累加和的结果。
累乘
定义一个变量,并赋初值为1;
用该变量累乘(*=)每一个数据项;
当访问完每一个数据项,此时该变量的取值就是累乘的结果。
极值(多应用于数组)
定义一个变量,并赋初值为第一个数据项;
从第二个数据项开始,依次于该变量进行比较,如果大于/小于该变量,则将当前数据项的数据赋值给该变量。
当访问完每一个数据项,此时该变量的取值就是求极值的结果。
循环的嵌套
3种循环(while、for、do..while)可以相互嵌套的。在前一个循环结构中又嵌套一个循环结构。例如:
案例
需求:九九乘法表
分析:
我们发现九九乘法表整体其实就是一个9行9列的直角三角形
同时发现:每一行显示的列的数量不超过所在行,第1行1列,第2行2列...
假定:行用i表示,列用j表示,i和j的关系:j <= i
在实现的时候,我们发现需要同时控制行和列的变化,在编程中,行列式需要通过for双层嵌套实现(双重for循环)。
代码:
/*************************************************************************> File Name: demo09.c> Author: 三道渊> Description: > Created Time: 2025年07月15日 星期二 17时28分10秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[]){printf("--- 九九乘法表 ---\n");// 外层循环:生成行,1~9for (int i = 1; i <= 9; i++){// 内层循环:生成列,1~(j <= i)for (int j = 1; j <= i; j++){// 生成算式printf("%dx%d=%d\t", j, i, j*i);// printf("*"); // 替换成这行代码,输出下直角三角形}// 每一行所有列生成完毕,一定要还行printf("\n");}printf("\n");return 0;}
运行结果:
案例
需求:求100~200之间的所有的素数(素数又被称作质数)
分析:
什么是素数:只能被1和自身整除的数叫做素数或者质数。
代码:
/*************************************************************************> File Name: deme10.c> Author: 三道渊> Description: > Created Time: 2025年07月15日 星期二 17时42分14秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[]){// 创建一个变量,存放100~200之间的自然数 int num = 100;// 定义一个循环变量iint i;// 定义标志位,1-素数,0-非素数int flag;// 外层循环:生成100~200之间的自然数 for (; num <= 200; num++){// 每一个num生成的时候,默认其为素数flag = 1;// 内层循环:我们让num 对 2~num/2 |(num -1)之间的数进行整除,一旦出现一次整除的情况,我们认定其为非素数for (i = 2; i < num / 2; i++){// 测试整除的情况if (num % i == 0){flag = 0;// 非素数// 循环结束,只要出现一次能整除的情况,就说明这个数不是素数。break是提前结束了循环break; }}// 输出校验结果if (flag) // 等价于 flag == 1printf("%-4d",num);}printf("\n");return 0;}
运行结果: