第3章C语言程序的控制结构3.6循环程序控制结构循环是重复执行某些语句(循环体)的一种结构。循环结构是C程序设计中的三种基本结构之一,灵活掌握循环结构对于编写高效简洁的程序至关重要。循环程序设计的特点是程序的执行顺序与程序书写的顺序相一致,而且在循环体上重复执行多次。在C语言中有while、do…while和for三种循环结构。循环结构有两种类型:①当型循环结构,流程图如图3-14所示,表示当P成立(为真)时,反复执行A语句块,直到条件P不成立(为假)时结束循环。②直到型循环结构,流程图如图3-15所示,表示先执行A语句块,再判断条件P是否成立(为真),若条件P成立(为真),则反复执行A语句块,直到条件P不成立(为假)时结束循环。当型循环结构当型循环结构直到型循环结构直到型循环结构3.6.1while语句一般形式为:while(表达式)循环体执行过程:先求表达式的值,当表达式值为非零(为真)时,则执行循环体中的语句(为清楚起见,循环体语句通常用一对花括号括起来,构成复合语句),接着再次判定表达式……,先判定表达式,再执行循环体,这个过程持续进行直到表达式的值为0时,结束循环。特点是:先判断表达式,后执行循环体语句。例3.19用while语句,求1+2+3+……+99+100分析:本题的关键是定义循环的判断条件,实现100个自然数求和。定义一个变量i,从1开始,每循环一次加上1,直到i100为止;再定义一个变量sum存放总和,将其清零,然后,每循环一次加上一个自然数到总和中。流程图如图所示。程序代码如下:#includestdio.hmain(){inti=1,sum=0;while(i=100){sum=sum+i;i++;}printf(“%d\n”,sum);}例3.20从键盘输入一个正整数,计算其阶乘。分析:本题的题意是要求n!,其中n从键盘输入。利用while语句实现循环时,关键是定义循环终止的条件,可以定义一个循环变量i,i从1开始,每循环一次加上1,直到in终止,while语句的表达式为i=n;再定义一个变量sum存放阶乘积,令sum的初值为1(思考:为什么不能为0?),每循环一次乘以一个i。程序代码如下:#includestdio.hmain(){inti=1,sum=1,n;printf(Pleaseinputn:);scanf(%d,&n);while(i=n){sum=sum*i;i++;}printf(sum=%d\n,sum);}关于while语句的说明:①循环体有可能一次也不执行;②循环体语句可以是任意类型语句;③遇到下列情况退出循环:条件表达式不成立(为0);循环体内遇break,return,goto;④无限循环:当条件表达式始终是非零时,while语句将无法停止。事实上,C语言程序员有时故意用非零常量作为条件表达式来构造无限循环。惯用形式为:while(1)循环体除非循环体含有跳出循环控制的语句(break、return、goto)或者调用了导致程序终止的函数,否则上述这种形式的while语句永远执行下去。3.6.2do…while语句一般形式为:do循环体while(表达式);分号不能少!执行过程:与while语句不同,do…while语句不管条件如何,至少先执行一次循环体内的语句,然后判断while后括号内表达式的值是否为真,若表达式的值非0,即为真,则继续重复执行循环体语句,然后再次计算表达式的值……,当循环体执行后,条件表达式的值变为0时,循环结束。特点:先执行循环体,然后判断循环条件是否成立。例3.21用do…while语句,求1+2+3+……+99+100分析:本题的关键是定义循环的终止判断条件,实现100个自然数求和。定义一个变量i,从1开始,每循环一次加上1,直到i100为止;再定义一个变量sum存放总和,将其清零,然后,每循环一次加上一个自然数到总和中#includestdio.hmain(){inti=1,sum=0;do{sum=sum+i;i++;}while(i=100);printf(sum=%d\n,sum);}可以看出,例3.19和例3.21程序运行的结果相同,但如果将循环变量i的初值改为101,例3.19的结果为0,而例3.21的结果为101。思考:为什么结果不同?例3.22用户任意从键盘输入一个整数,计算其数字的位数。分析:此题的算法是把输入的整数反复除以10,直到结果变为0停止,除法的次数就是这个整数的位数。因为不知道需要多少次除法运算才能达到0,所以程序中需要执行某种循环,用while语句还是do…while语句?显然do…while更合适,因为每个整数,甚至是0,都至少有一位数字。程序代码如下:#includestdio.hmain(){intdigits=0,n;/*digits为数字位数,n为输入的整数*/printf(Enteranonnegativeinteger:);scanf(%d,&n);do{n/=10;digits++;}while(n0);printf(Thenumberhas%ddigit(s).\n,digits);}程序运行时,输出结果为:Enteranonnegativeinteger:60↙Thenumberhas2digit(s).思考:如果用相似的while循环替换do…while循环会发生什么?当n初始值为0时,结果会一样吗?3.6.3for语句for语句是C语言循环结构中功能最为强大、应用最为灵活、最为广泛的一种形式,它不仅适用于循环次数确定的情况,也适用于循环次数未知的情况。while语句和do…while语句循环均可转换成for循环的形式。一般形式为:for(表达式1;表达式2;表达式3)循环体说明①for是关键字。②for之后的圆括号内一共有三个表达式,以分号隔开。一般情况下,表达式1的作用是赋初值;表达式2的作用是控制循环,即循环条件;表达式3的作用是修改循环变量的值,一般是赋值。③循环体语句如果只有一条,可以不加花括号;如果循环语句超过一条,则必须加花括号组成复合语句。④圆括号内的三个表达式在语法上都可以省,但两个“;”不能省。执行过程:先执行表达式1;再判断表达式2是否为0,若不为0,则执行循环体语句,执行表达式3,再重新计算表达式2;若表达式2为0,则退出for循环。图3-19for循环流程图for语句和while语句关系密切。事实上,除极少数情况外,for语句总可以用等价的while语句替换:表达式1;while(表达式2){循环体语句;表达式3;}图3-19for循环流程图例3.23用for语句,求1+2+3+……+99+100分析:本题用for循环,关键是定义三个表达式,根据题意,要求100个自然数之和,所以定义循环变量i,i初值为1(表达式1:i=1),每循环一次循环变量加1(表达式3:i++),循环一共执行100次,(表达式2:i=100),循环体语句就是累加循环变量的当前值(sum+=i)。程序代码如下:#includestdio.hmain(){intsum,i;sum=0;for(i=1;i=100;i++)sum+=i;printf(sum=%d\n,sum);}说明:在整个for循环的执行过程中,表达式1只计算一次,表达式2和表达式3则可能计算多次,循环体可能多次执行,也可能一次都不执行,要视条件而定。其中:①表达式1可省略,此时应在for语句前给循环变量赋初值。如:i=1;for(;i=100;i++)sum+=i;②表达式2可省略,即不判断循环条件,循环将无终止的进行下去,需要在循环体中用break语句退出循环。如:for(i=1;;i++){if(i100)break;sum=sum+i;}③表达式3也可省略,但应在循环体中让循环变量产生变化,保证循环能正常结束。如:for(i=1;i=100;){sum+=i;i++;}④表达式1和3可同时省略,只给出表达式2(循环条件)。如i=1;for(;i=100;){s=s+i;i++;}⑤三个表达式都可省略,但分号不能少。此时在循环体中需要使用相关语句保证循环结束。如:i=1;for(;;){if(i100)break;sum+=i;i++;}⑥循环体可以是空语句,空语句用于延时。for(s=0,i=1;i=100;s=s+i,i++);例3.24打印Fibonacci数列。Fibonacci数列为:1,1,2,3,5,8,13,21,34,…它的规律是:从第3项开始,每一项都是其前两项之和。分析:根据上述规律,假设c为所求的项,a为其前二项(a的初值为1),b为其前一项(b的初值为1),则c=a+b(从第3项开始)。因此,迭代公式为:c=a+b;a=b;/*为下一次迭代作准备*/b=c;然后通过循环不断迭代,得出所求数列。#includestdio.hmain(){inta,b,c,n,i;a=1;b=1;printf(Entern:);scanf(%d,&n);/*输入数列的打印项数*/printf(%10d%10d,a,b);/*打印第1项和第2项*/for(i=3;i=n;i++)/*求数列的第3~n项*/{c=a+b;/*求第i项*/a=b;/*为下一次迭代作准备*/b=c;printf(%10d,c);/*输出第i项,右对齐*/if(i%5==0)printf(\n);/*每行输出5项*/}printf(\n);}程序运行时,输入:40↙输出结果如下图:3.6.4循环结构的嵌套循环结构中的循环体语句可以是任何合法的C语句,while、do…while、for语句是合法的C语句,当然也能出现在循环体中,这种循环体中又包含了另一个完整的循环结构,称为循环的嵌套。内嵌的循环还可以嵌套循环,这就是多层循环。while、do…while、for三种循环可以相互嵌套,层数不限。但最常用的是二重循环或三重循环,如果嵌套的层数更多,会增加程序阅读、理解的困难,给程序调试带来麻烦。嵌套循环时,外循环必须完整地包含内循环,不能相互交叉。外层循环内层循环内层循环内层循环合法的嵌套循环嵌套循环的执行过程:首先执行外循环,然后执行内循环,外循环每执行一次,内循环就完整地执行一遍。若内循环中还存在嵌套的循环,则进入下一层嵌套循环结构中,顺序执行有关的循环结构。若在某层循环结构中嵌套了两个或多个并列的循环结构,则从外循环进入时,顺次执行这些并列的循环结构。例如:输出如下图所示图形,可以用单层循环实现:#includestdio.hmain()*****{*****inti;*****for(i=1;i=5;i++)*****printf(“*****\n”);*****}语句“printf(“*****\n”);”的功能等价于以下的循环结构:for(j=1;j=5;j++)printf(“*”);printf(“\n”);进行一个等价替换,就可以得到以下的嵌套循环:#includestdio.hmain(){inti,j;for(i=1;i=5;i++){for(j=1;j=5;j++)printf(“*”);printf(“\n”);}}例3.25整元换零钱问题。把1元兑换成1分、2分和5分的硬币,共有多少种不同换法?(每种至少一个)分析:本题是一个穷举法问题。穷举是一种重复型算法。它的基本思想是,对问题的所有可能状态一一测试,直到找到解或者将全部可能状态都测试过为止。设5分硬币的可能个数为i,则i的取值范围应为1~19;设2分硬币的可能个数为j,则j的取值范围应为1~(100-i*5)/2-1;而1分的硬币个数应是100-5*i-2*j。设共有m种换法。为了方便阅读和计数,每10种换法中间空一行。#includestdio.hmain(){inti,j,m;m=0;for(i=1;i=19;i++)for(j=1;j=(100