任务描述
主要介绍循环结构。循环结构是指在程序中需要反复执行某个功能而设置的一种程序结构。它由循环体中的条件,判断继续执行某个功能还是退出循环。根据判断条件,循环结构又可细分为以下两种形式:先判断后执行的循环结构和先执行后判断的循环结构。
循环结构可以减少源程序重复书写的工作量,用来描述重复执行某段算法的问题,这是程序设计中最能发挥计算机特长的程序结构。循环结构可以看成一个条件判断语句和一个向回转向语句的组合。
知识学习
前面曾让大家思考过这样一个问题:要从多个数据中找出最大值,当时的解决思路是依次两两比较,这样参与比较的数据越多,用到的类似代码if(max <c)max =c;也就越多,这样对于程序的阅读是很不利的。在C语言中,如果某段代码重复出现有限次数,就可以借助循环来实现。
比如实现从输入的10 个数中找到最大值的程序,用循环实现代码如下:
程序结果如图3.23所示。
图3.23 统计10 个数中的最大值
这段代码肯定要比写9 次“if(max<c)max=c;”代码段要方便。
循环结构是程序中一种很重要的结构。其特点是,在给定条件成立时,反复执行某程序段,直到条件不成立为止。给定的条件称为循环条件,反复执行的程序段称为循环体。C语言提供了3 种循环语句,可以组成各种不同形式的循环结构。
①while 语句;
②do-while 语句;
③for 语句。
(1)while 语句
while 语句的功能:计算表达式的值,当值为真(非0)时,执行循环体,然后重复上述过程,一直到表达式的值为假(0),循环结束,执行循环体后面的语句。其语法格式为:
图3.24 while 语句流程图
程序结果如图3.25所示。
图3.25 累加和
使用while 语句应注意以下几点:
①while 语句中的表达式一般是关系表达或逻辑表达式,只要表达式的值为真(非0)即可继续循环。
②循环体如包括有一条以上的语句,则必须用{}括起来,组成复合语句,因为while 函数也需要满足“独生子女规则”。
不要在while()的后面直接出现分号“;”,这样会导致循环体变成了一条空语句。后面的内容就被看作while 循环体后的语句了。这里有一个关于程序员的故事:某程序员编写了一个程序向自己倾慕已久的女神表白,程序如下。
这个女神也是个程序员,她在while(forever)的后面加了一个分号“;”后返回给了当事人。你认为该程序员表白成功了吗? 为什么?
③应注意循环条件的选择以避免死循环。循环前,要给控制变量赋初值,保证能进入循环体内,否则也没有意义了;循环体中,要让循环控制量发生向边界偏移的变化。
比如在上个程序的循环体中,丢失了“i++;”这条语句,i 始终为1,则1 <10 永远成立,这样程序就出现了死循环。
再来看一个数列和的例子,求1/2、2/3、3/4…的前20 项和:
程序结果如图3.26所示。
图3.26 转浮点型的循环
使用循环,关键就是要找到进入循环体的入口,以及循环体的出口条件。
(2)do-while 语句
在该节一开始就用该结构完成了一个求10 个数中最大值的程序。
do-while 语句的一般形式为:
该结构的流程图如图3.27所示。
do-while 语句的执行过程:执行循环体中的语句,然后判断条件,条件成立再执行循环体;重复上述过程,直到条件不成立时结束循环。do-while 语句的特点:当一开始条件就不成立时,已经执行了一次循环语句。
图3.27 do-while 语句流程图
对于do-while 语句还应注意以下几点:
①在if 语句和while 语句中, 表达式后面都不能加分号,而在do-while 语句的表达式后面则必须加分号,不要省。
②do-while 语句和while 语句的区别在于do-while 是先执行后判断,因此do-while 至少要执行一次循环体。而while 是先判断后执行,如果条件不满足,则一次循环体语句也不执行。
③在do 和while 之间的循环体由多个语句组成时,也必须用{}括起来组成一个复合语句。
④do-while 和while 语句相互替换时,要注意修改循环控制条件,do-while 语句也可以组成多重循环,而且也可以和while 语句相互嵌套。
在求两数的最大公约数时,有个经典的算法被称“辗转相除法”,其算法思想如下:
①用m 除n,获取余数r;
②然后将n 值给m,将r 值给n;
③重复以上两步,直到r 为0,当前m 值就是最大公约数。
用do-while 来实现该算法:
程序结果如图3.28所示。
图3.28 do-while 求最大公约数
在该程序的基础上,要想求出两数的公倍数,必须把原始输入的两个值保存下来,比如m1 =m;n1 =n;那么最小公倍数为m1*n1/m。
(3)for 语句
for 语句是C语言所提供的功能更强,使用更广泛的一种循环语句,也是最为灵活的一种循环语句。它不但可以用于循环次数确定的情况,也可以用于循环体次数不确定的情况。其一般形式为:
表达式1 通常用来给循环变量赋初值,一般是赋值表达式。也允许在for 语句外给循环变量赋初值,此时可以省略该表达式。
图3.29 for 循环流程图
表达式2 通常是循环条件,一般为关系表达式或逻辑表达式。
表达式3 通常可用来修改循环变量的值,一般是赋值语句。
这3 个表达式都可以是逗号表达式,即每个表达式都可由多个表达式组成。3 个表达式都是任选项,都可以省略。一般形式中的“语句”即为循环体语句。
1)for 循环的应用
for 语句的语义是:
①首先计算表达式1 的值。
②再计算表达式2 的值,若值为真(非0)则执行循环体一次,否则跳出循环。
③然后再计算表达式3 的值,转回第2 步重复执行。
④循环体结束,执行for 语句下面一条语句。
在整个for 循环过程中,表达式1 只计算一次,表达式2 和表达式3 则可能计算多次。循环体可能多次执行,也可能一次都不执行。
其执行过程的流程如图3.29所示。
之前用while 循环编写的求前100 项整数的和的核心代码可以用for 语句替换:
在while 中可以找到对应的表达,所以可以把for 循环的一般形式借助while 语句中的说法改成更好理解的表达:
前面用辗转相除法完成了对两个最大公约数的求解问题,这里再用普通的算法来实现一下,两数的最大公约数是指能同时被两数整除的最大值。
分析:这个最大公约数一定处在1 到两数较小者的范围内,换句话说,最大公约数最小是1,最大是两者中的较小者。由于无法确定,只能从1 开始去试,有1 个能同时整除的就把它存到最大公约数变量中,查找完毕后,当前存储的值就是最大公约数。
(www.xing528.com)
程序结果如图3.30所示。
图3.30 for 循环求最大公约数
打印所有“水仙花数”,所谓“水仙花数”,是指一个3 位数,其各位数字的立方和等于该数本身。比如153 =13+53+33=1 +125 +27,所以153 是1 个水仙花数。
分析:首先水仙花数是1 个三位数,由此确定了遍历的范围100 ~999。然后就要把遍历的每一个数都分解成3 个数,并求出三者的立方和,接着判断该值是否与本身相同,相同输出,不同不处理。
程序结果如图3.31所示。
图3.31 水仙花数
接下来再次回到求前100 项的和,如果把程序中的加号“+”换成乘号“*”,当然sum 的初始值不能再是0,否则不管多少项最终的结果都会是0,把它修改为1。读者思考一下,这个程序实现了什么功能?
程序结果如图3.32所示。
图3.32 求阶乘
这个程序求解小范围内的阶乘是没有任何问题的,但是阶乘在后期的增长是很可怕的,这也就给程序埋下了一个bug,当执行20 的阶乘时,结果如图3.33所示。
图3.33 整型越界溢出
这肯定是一个错误的结果,因为不可能出现负值。回想之前的int 类型知识,应该会想到这应该是数据溢出导致的,换句话就是说这个时候的值,已经不能用int 来定义了,可以把jiecheng变量换成double 类型的,但是输出时不能原样输出,要控制小数点后面的位数为0。
程序结果如图3.34所示
通过本例希望再次提醒读者数据的范围问题是编程前必须思考的,也告诉了读者处理的一种方法。
2)for 循环的省略形式
for 循环中的“表达式1(循环变量赋初值)”“表达式2(循环条件)”和“表达式3(循环变量增量)”都是选择项, 即可以缺省,但“;”不能缺省。
图3.34 修正值越界
①省略了“表达式1(循环变量赋初值)”, 表示不对循环控制变量赋初值。
②省略了“表达式2(循环条件)”, 则不做其他处理时便成为死循环。
例如:
③省略了“表达式3(循环变量增量)”, 则不对循环控制变量进行操作,这时可在语句体中加入修改循环控制变量的语句。
例如:
④省略了“表达式1(循环变量赋初值)”和“表达式3(循环变量增量)”。
例如:
⑤3 个表达式都可以省略。
例如:
3)for 循环扩展形式
①表达式1 可以是设置循环变量的初值的赋值表达式,也可以是其他表达式。
例如:
②表达式1 和表达式3 可以是一个简单表达式也可以是逗号表达式。
③表达式2 一般是关系表达式或逻辑表达式,但也可是数值表达式或字符表达式,只要其值非零,就执行循环体。
例如:
表达式2 是先获取一个字符,然后赋给c 变量,只要该字符不是回车符号,循环就会一直执行。
请思考一下,3 种循环语句它们的区别在哪?
(4)循环嵌套
一个循环语句的循环体内包含另一个完整的循环语句,称为循环的嵌套。while 语句、dowhile 语句和for 语句都可以互相嵌套,甚至可以多层嵌套,常见的组合形式如下:
①while 嵌套while;
②do-while 嵌套do-while;
③for 嵌套for;
④while 嵌套do-while;
⑤for 嵌套while。
当然根据情况,读者也可以进行自由组合,对于循环嵌套来说,常常解决图形显示问题和数学问题。
首先看一个用“*”拼成金字塔图形的例子,金字塔的层数通过输入控制,效果如图3.35所示。
图3.35 形成图案
分析:该金字塔图形可以由行和列来组成,要显示出来需要一行一行的打印,而每一行中都是由前面的空格字符和后面的星号组成的。每一行星字符的个数跟所在行数的关系是2*i-1。前面空格字符的个数是总行数减去当前的行数,所以可以用外层循环控制要显示的金字塔行数,用两个内层循环分别去控制每行的空格和星号个数。
对于双重循环或者多重循环,内层循环必须被完全包含在外层循环中,不得交叉,内外循环的控制变量尽量不同,否则会造成程序的混乱。对于双重循环,外层循环执行一次,内层循环执行若干次,也就是说内层循环结束后,才能进入外层循环的下一次。所以内层循环变化快,外层循环变化慢。
九九乘法表的程序实现,效果图如图3.36所示。
图3.36 九九乘法表
程序代码如下:
数学里面有很多经典问题都可以借助循环嵌套来求解,比如鸡兔同笼、百元买百鸡等问题。因为鸡兔同笼一般是唯一解,这里就先不探讨了,《算经》中提出的百鸡问题,就是鸡翁一只五钱,鸡母一只三钱,鸡雏三只一钱。用一百钱买一百只鸡,请问鸡翁、鸡母、鸡雏各买多少只?
分析:从数学的角度,根据题意可以列出两个方程x +y +z =100;5x +3y +z/3 =100,但是有3 个未知数,所以解是不唯一的。
只能从各种可能逐个去验证,鸡翁能取得值是0 ~20,鸡母能取到的值是0 ~33,鸡雏是100 减去前两个,但是还必须能被3 整除。
程序结果如图3.37所示。
图3.37 百元买百鸡
有些初学者在解决这个问题时,可能把if 的表达式只写100 ==5*x +3*y +z/3,结果会多出几种可能。原因在于z/3 得到是一个整数,它的结果可以与附近满足条件值是一样的,如果只写这样一个判断表达式,需要改成z/3.0 用浮点数去比较,结果如图3.38所示。
图3.38 错误操作结果
任务总结
一般情况下,循环语句可以3 种灵活使用,相互代替。从使用频率上来说for 循环是最多的,并且它最灵活,其中do…while 循环是使用最少的。
while 语句是先判定条件为真后再执行循环操作,而do…while 循环是先无条件地执行一次循环操作后再判定条件,如果为真则继续执行循环操作。
在分析程序中,当涉及范围等情况时,可以考虑使用for 循环。for 循环严格要求书写格式:for(初始化;循环条件;变化规律)。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。