目录
- 1.浅析循环结构
- 2.循环结构的介绍
- 2.1 while循环
- 2.2 for循环
- 2.3 do_while循环
- 3.案例解析——猜字谜游戏
1.浅析循环结构
上一篇我们学习了分支结构,完成了关机病毒程序,里面我们用到了一个简单又经典的循环结构。
循环结构、顺序结构、分支结构都是我们生活的一种抽象化。例如我们大多数同学都要经历学校——工作——退休,这是一种顺序结构;在学校上学或者参加工作,我们可以选择不同的学校和不同的城市,这是一种分支结构;在学校我们要日复一日的读书,上班了我们要日复一日的工作,退休了我们要日复一日的等待残生了却。再往大了看,一代又一代的人都要经历受教育——社会建设——退休,这里面的循环往复又岂止眼前之语。
2.循环结构的介绍
我们通常使用的循环结构有:for循环、while循环、do_while循环。
2.1 while循环
while(表达式){语句}或
while(表达式)单个语句但后者却不是好的代码风格,我们应该切记C语言程序需要具备可读性与可扩展性。
while循环的执行流程:
(continue——跳出本次循环,break——终止循环)
while语句先对表达式进行判断,表达式为真,即结果为非零数或true (布尔类型),则执行语句。一直为真则循环执行,直到为假。
给出代码示例并分析如下:
#include<stdio.h>// 打印1-10intmain(){inti=1,input=0;scanf("%d",&input);// 记录用户的输入while(i<11)// 当i<11,即i为1到10的数时表达式结果为真,返回值不为0,循环执行{if(i==input)// 当i等于input时跳出循环,停止打印{break;}printf("%d ",i);// 打印 ii=i+1;// 每次循环结束,i加1}return0;}(输入11,i永远不可能等于input,可不break,正常走完循环次数,1到10全部打印)
输入:11结果:1 2 3 4 5 6 7 8 9 10
(输入6,则打印6之前break跳出循环)
输入:6结果:1 2 3 4 5
让我们来分析一下这个代码。
上例中,我们设置while循环的表达式为i < 11,则最多打印到10。
每次循环结束i加1,所以我们想正常打印1~10,就要避免break提前跳出循环,必须输入一个大于10的数。
如果我们不想打印到10,想提前break结束打印,需要注意,break的if判断写在打印之前,所以输入值等于最终打印的数字加1。这里最终的目标数是5,即需要打印1到5,则输入值为6。
如果我们把break跳出循环,换成continue跳出当次循环,再输入相同的输入值11、6,会是什么结果?
2.2 for循环
for循环是最常用的循环语句,我们来看看它的语法示例:
for(表达式1;表达式2;表达式3;){语句}// 相同的,单个语句也可以不带花括号{},直接跟在for之后其中:
表达式1用于循环变量的初始化,如int i = 0,且循环变量的初始化只执行一次。表达式2用于循环条件的判断,如i < 10,如果表达式2结果为0循环结束,否则继续。表达式3用于循环变量的调整,如i++或i+=2。循环变量的初始化和循环条件一起决定了循环变量的范围,在此基础上,调整则影响了循环次数。
我们来看看执行流程:
现要求打印一个腰长为10的等腰直角三角形,如下图所示。
#include<stdio.h>intmain(){// 和if嵌套类似,套娃for(inti=0;i<10;i++)// 外循环:i代表第i+1行,一次外循环代表一行{// 内循环:j的取值范围代表星号*个数,j的范围随外循环次数增加而减少for(intj=0;j<10-i;j++){printf("* ");}printf("\n");// 每次外循环结束,一行打印完毕,打印换行符,进入下一行即下一遍外循环}return0;}我们可以看到,无论是外循环还是内循环都只执行了一次循环变量的初始化。往后都是表达式2(判断循环是否继续)——语句(继续循环,执行语句)——表达式3(调整循环变量)的循环往复,直到表达式2结果为0,循环结束。
2.3 do_while循环
do while是三种循环语句中使用最少的,但在某些情况下,它也很有用(Tips:其实goto语句也可以实现循环结构,但万一使用不当,后果很严重)。
下面我们来看看它的语法结构:
do{语句;}while(表达式);while和for这两种循环都是先判断,满⾜条件就进⼊循环,一旦不满⾜就结束循环。
do while是先进⼊循环体,执⾏循环语句,再判断。表达式为真,则进入第二次循环;表达式为假,结束循环。
其执行流程如下:
do_while通常需要我们在循环体外初始化循环变量。
do_while是先执行再判断,所以do_while循环至少执行一次,这是它的特殊所在。
代码示例如下:
#include<stdio.h>intmain(){// 打印1-10的数inti=1;// 循环体外初始化循环变量do{// 先执行printf("%d ",i);i=i+1;// 调整循环变量}while(i<11);// 再判断:当i >= 11,终止循环,不再打印return0;}结果:
3.案例解析——猜字谜游戏
到这里我们已经学完了三大结构。虽然下面有些琐碎的知识,但会用即可,我们直接来尝试完成一个猜字谜游戏。
先给出完整代码(需要自取,否则跳过看详解):
#include<stdio.h>#include<windows.h>#include<stdlib.h>//提供rand函数,功能为基于某个数(种子)生成随机数;srand函数设置、改变种子#include<time.h>//提供time函数,用于获取一个时间戳,可作为种子voidmenu()//菜单函数,提供菜单功能{printf("**************\n");printf("****1.Play****\n");printf("****0.Exit****\n");printf("**************\n");}voidgame()//game函数,提供游戏功能{//times规定并记录游戏次数,guess存储用户的输入值,r为随机数且此处被赋值范围为1~100inttimes=5,guess=0,r=rand()%100+1;while(times){scanf("%d",&guess);if(guess>r){printf("太可惜了,你猜大了!\n");}elseif(guess<r){printf("太可惜了,你猜小了!\n");}else{printf("芜湖~,恭喜你猜对了!!!\n");break;}times--;if(guess>r||guess<r)//打印剩余猜测次数{printf("你只剩下%d次机会啦!\n",times);}}if(times==0)//一轮游戏结束,打印正确答案{printf("\n猜错了哦,正确答案应该是%d\n",r);}}intmain(){printf("哼哼( ̄、 ̄),大笨蛋赶快看清楚本大爷的名字,本大爷叫作“猜数字游戏”,你应该知道怎么玩。\n");intinput=0;//存储输入值,用于开始游戏或结束游戏srand((unsignedint)time(NULL));do{menu();printf("请选择:>");scanf("%d",&input);system("cls");//系统命令:清屏if(input==1){printf("\n开始游戏噜!!!赶快输入你猜的数字吧:\n");}switch(input)//代码核心部分{case1:game();break;case0:printf("\n游戏结束了,下次再来吧\\(n.n)/Bye~\n");break;default:printf("输入错误,请重新输入\n");break;}}while(input);return0;}分析:首先从main函数起始
intmain()//程序入口{// 提前说明游戏,可写入menu一起打印,亦可与菜单函数结合并拓展,制作跳转到另一个游戏说明页面功能printf("哼哼( ̄、 ̄),大笨蛋赶快看清楚本大爷的名字,本大爷叫作“猜数字游戏”,你应该知道怎么玩。\n");intinput=0;// 存储用户输入值,即0或1,开始或结束游戏srand((unsignedint)time(NULL));// time函数获取并返回时间戳,强制类型转换为无符号整型,再用srand设置种子// do_while循环:走来先打印游戏菜单界面,让玩家用户选择开始或结束do{menu();// 打印游戏菜单// 用户进行输入printf("请选择:>");scanf("%d",&input);// 根据输入,开始游戏或结束游戏system("cls");// 系统命令:清屏。开始游戏需要打印游戏界面if(input==1)// 如果开始游戏,就打印一些欢迎话术,增加一点氛围感{printf("\n开始游戏噜!!!赶快输入你猜的数字吧:\n");}switch(input)// 游戏主体模块,分支结构{// 都加了break,只执行匹配的case,避免分支重叠case1:game();break;// input为1,开始游戏,执行game函数case0:printf("\n游戏结束了,下次再来吧\\(n.n)/Bye~\n");break;// input为0,游戏结束default:printf("输入错误,请重新输入\n");break;// 其他非法输入,但input不为0,提示错误信息}}while(input);// 开始游戏,input为1;非法输入,input不为0。两个都是非0数,都继续循环重新开始return0;}main函数可分为三个核心步骤:菜单函数—>scanf—>选择与分支(三个分支:开始游戏、结束游戏、选择错误)。
游戏开始需要给出一个菜单。我们可以先写一个简单的函数menu(这里涉及函数知识,可以不用管,按照示例代码写即可)。
voidmenu()// 菜单函数,提供菜单功能{printf("**************\n");printf("****1.Play****\n");printf("****0.Exit****\n");printf("**************\n");}下一环节需要scanf,通过用户的输入选择分支。第三个环节开始之前,我们可以先使用system(“cls”)清屏保证接下来的游戏画面清爽。
在第三个环节,需要考虑玩家非法输入的可能,我们应该给用户重新选择的机会,再走一遍流程,即menu—>输入—>分支。
因此,input有三种可能情况,可以用switch来设计一个多分支结构:case 1、case 0 和 default。
case 1进入具体的游戏逻辑;
case 2需要游戏结束的话术;
default需要非法输入的错误提醒。
以上3种情况不能有分支重叠,都加个break直接结束switch语句。
可以发现,游戏开始和非法输入,全部流程结束之后,都需要进入下一轮循环,让用户重新选择开始或结束游戏。
因此,特别将input设计为0,此时游戏结束,do while循环结束。
不管用户选择游戏开始,input 为 1,还是用户非法输入,input都是非零值,进入do while的第二次循环。
以上是main函数所有设计。我们只需要补充game函数就大功告成。
代码如下:
voidgame()// game函数,提供游戏功能{// times规定并记录游戏次数,guess存储用户的输入值,r为随机数且此处被赋值范围为1~100inttimes=5,guess=0,r=rand()%100+1;while(times){// 当times减小到 0停止循环,一轮游戏结束scanf("%d",&guess);if(guess>r)// if else语句用于判断是否猜中数字{printf("太可惜了,你猜大了!\n");}elseif(guess<r){printf("太可惜了,你猜小了!\n");}else{printf("芜湖~,恭喜你猜对了!!!\n");break;}times--;// 调整循环变量if(guess>r||guess<r)// 打印剩余猜测次数{printf("你只剩下%d次机会啦!\n",times);}}if(times==0)// 循环结束,即一轮游戏结束,打印正确答案{printf("\n猜错了哦,正确答案应该是%d\n",r);}}核心流程大致如下:输入猜测值 —> 判断 —> 给出提示/反馈(三个分支 ‘ > ’、‘ == ’、‘ < ’)。
很明显这三个流程属于一个循环过程,我们还需要一个值来代表可循环次数——可猜测次数。
我们可以先在循环体外,进行游戏次数times的初始化。
游戏开始,先scanf等待用户输入,再对输入值判断,提示玩家猜测值大了或者小了。如果等于,猜成功了就结束,直接break终止循环。
如果玩家一直没猜中,需要考虑次数是否耗尽,可以直接告诉玩家正确答案然后结束。
game函数完毕,返回main函数。