说实话,循环结构在C语言里算不上最难的东西,指针比它绕多了。但你要说它重不重要?我跟你讲,任何稍微有点实际意义的程序,都离不开循环。你想想,让你算1加到100,没有循环你不得写到天荒地老?让你从一堆数据里找到最大值,没循环你只能干瞪眼。
很多人学循环的时候觉得“这有啥难的,不就是while和for嘛”,结果一到写代码就翻车——死循环跑得电脑嗡嗡响,或者循环次数怎么都对不上。说白了就是没真正理解循环背后的执行逻辑,光记住语法格式了。
下面我把C语言里跟循环相关的知识点从头捋一遍,配上几个典型例子,你看完要是还迷糊,来找我。
C语言在线编程环境:https://sky315.com:8080/c/。
一、三种循环结构,各自什么来头?
C语言提供了三种循环:while、do-while、for。它们都能实现重复执行,但各有各的脾气。
while循环——先看门再进门
语法格式长这样:
while (条件表达式) { 循环体语句; }
执行逻辑就一句话:先判断条件,条件为真就进循环体跑一遍,跑完再回来判断,直到条件为假跳出。如果一开始条件就是假的,循环体一次都不执行。
do-while循环——先进门再说
do { 循环体语句; } while (条件表达式);
跟while唯一的区别:先执行一次循环体,再判断条件。也就是说,不管条件成不成立,do-while至少给你跑一次。这个特性有时候特别好使。
for循环——计数器的好兄弟
for (表达式1; 表达式2; 表达式3) { 循环体语句; }
for循环的三个表达式各司其职:
-
表达式1:初始化,在循环开始前执行一次
-
表达式2:循环条件,每次循环开始前判断,真就执行,假就退出
-
表达式3:每轮循环结束后执行,通常用来更新循环变量
执行顺序是:1→判断2→真就进循环体→执行3→再判断2→真再进循环体→执行3……直到判断2为假。
很多人搞不清楚的是,表达式1只执行一次,别以为它每轮都跑。还有,三个表达式都可以省略,但两个分号不能省。for(;;)就是一个死循环,跟while(1)一个意思。
二、循环控制语句——break和continue,别搞混了
这两个关键字是专门用来在循环内部“搞事情”的,很多人用混了,后果天差地别。
break:直接跳出当前循环,循环结束,继续执行循环后面的代码。注意是“跳出整个循环”,不是只跳一次。
continue:结束本轮循环,跳过本轮循环体后面还没执行的语句,直接进入下一轮循环。循环没结束,只是这次不干了,下次继续。
区别说白了就是:break是“我不玩了,走了”,continue是“这次我不干了,下次再说”。
三、典型例题挨个讲,比光看语法管用多了
光说语法你记不住,咱们上例子。这几个例子都是最经典的,你挨个看懂,循环的基本功就差不多了。
例1:计算1+2+3+...+100的和
这个题虽然简单,但三种循环写法你都得会,体会它们的不同。
用for循环:
int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; }
用while循环:
int sum = 0, i = 1; while (i <= 100) { sum += i; i++; }
用do-while循环:
int sum = 0, i = 1; do { sum += i; i++; } while (i <= 100);
你看,for循环把初始化、条件、更新三件事搁一块了,最紧凑。while把条件放前面,do-while先干再说。结果都一样,但for循环最适合这种“知道要循环多少次”的场景。
例2:从键盘输入一堆整数,以0结束,求平均值
这个例子有意思,因为循环次数不确定,你事先不知道用户会输几个数。
int num, sum = 0, count = 0; float avg; printf("请输入整数,输入0结束:\n"); while (1) { scanf("%d", &num); if (num == 0) { break; } sum += num; count++; } if (count > 0) { avg = (float)sum / count; printf("平均值:%.2f\n", avg); }
为什么用while(1)?因为循环次数未知,用无限循环配合break退出更灵活。用户输0的时候break直接跳出去,干净利落。
例3:输出1到100之间所有能被3整除的数
这题考的是continue的用法。
for (int i = 1; i <= 100; i++) { if (i % 3 != 0) { continue; // 不能被3整除?跳过这次,不打印 } printf("%d ", i); }
i=1的时候,1%3不等于0,continue一执行,后面printf就不跑了,直接进入i=2的下一轮。i=3的时候,3%3等于0,条件不成立,continue不执行,正常打印。continue在这里就像个筛子,把不符合条件的筛掉。
你不用continue也能写:
for (int i = 1; i <= 100; i++) { if (i % 3 == 0) { printf("%d ", i); } }
但有时候用continue能让代码逻辑更清晰——先排除特殊情况,再处理正常逻辑。
例4:打印9×9乘法表
这题考的是循环嵌套,就是循环里面再套循环。
for (int i = 1; i <= 9; i++) { for (int j = 1; j <= i; j++) { printf("%d×%d=%-4d", j, i, i * j); } printf("\n"); // 每行结束换行 }
外层循环控制行数(1到9行),内层循环控制每行打印几个式子。注意内层条件是j <= i,不是j <= 9——这样打印出来是下三角形状,j不会超过i。你要是写成j <= 9,那就打印成矩形了,乘法表就不是那个味儿了。
例5:判断一个数是不是素数
素数就是只能被1和自身整除的数。这题考的是循环什么时候结束。
int num, i, flag = 1; scanf("%d", &num); if (num <= 1) { flag = 0; // 1和负数不是素数 } else { for (i = 2; i < num; i++) { if (num % i == 0) { flag = 0; break; // 找到一个因子就够了,不用再找了 } } } if (flag == 1) { printf("%d是素数\n", num); } else { printf("%d不是素数\n", num); }
这里的break用得妙。一旦发现num能被某个i整除,说明它不是素数,后面不用再判断了,直接break跳出。你要是不用break,循环会一直跑到i=num-1,虽然结果一样但效率低不少。
优化一下:其实循环条件可以写成i <= sqrt(num),因为如果一个数有因子,必定有一个因子不大于它的平方根。这个优化思路你以后学了数学函数就知道了。
四、几个容易踩的坑,提前告诉你省得你折腾
坑1:分号放错地方
for (i = 1; i <= 100; i++); // 注意这个分号! { sum += i; }
for后面直接跟了个分号,循环体就成了空语句。循环默默跑了100次,啥也没干,然后才执行后面大括号里的sum += i,这时候i已经是101了。这种Bug编译器不报错,肉眼还不好找,特别恶心。
坑2:死循环
while (i <= 100) { sum += i; // 忘记写 i++; }
i永远不变,条件永远为真,电脑风扇转得飞起,程序永远不结束。写while循环一定记得更新循环变量。for循环这方面好一些,更新条件写在表达式3里,不太容易忘。
坑3:浮点数当循环条件
for (float x = 0.0; x != 1.0; x += 0.1) { // 这循环可能停不下来 }
浮点数在计算机里存的是近似值,0.1累加10次可能变成0.999999而不是1.0,x != 1.0永远为真。循环条件最好用整数,或者用<、>关系运算符,别用==或!=。
坑4:break只跳一层循环
嵌套循环里,break只能跳出它所在的那一层循环,外层的继续跑。想一次性跳出来?要么用goto(不推荐),要么加标志变量。
五、总结几句
循环结构说白了就三条:while先判断再执行,do-while先执行再判断,for适合固定次数。break是直接跳出来不玩了,continue是这次不干了下次再说。
你把这几个例子自己敲一遍,跑一跑,把循环次数、变量变化过程在脑子里过一遍,能做到看一段循环代码就知道它执行几次、每轮变量变成啥,那你循环这块就算彻底过关了。
别光看我写的,打开编译器,自己动手。代码这东西,看十遍不如敲一遍出一次错,出一次错修好了顶你看十遍。去练吧。