第5 章循环结构 【学习要点】 在程序设计中,如果一组操作需要反复执行,可用循环结构实现。C语言中实现循环 结构的控制语句有三种:while语句、do-while语句和for语句。这三种语句在功能和用 法上各有特点,熟练掌握将有助于复杂程序的编写。 ● 循环语句:while语句、do-while语句、for语句。 ● 流程控制语句:break语句、continue语句、goto语句。 ● 嵌套循环。 5.1 while语句 5.1.1 while语句 while语句用于实现当型循环结构,语法格式为: while(条件) 循环体 图5.1 while语句的流程图 while语句的执行过程是:先判断循环条件,如果条件成 立(值不为0)则执行循环体,然后重复“判断条件-执行循环 体”的过程,直到循环条件不成立(值为0)时退出while语句, 结束循环,然后执行while语句的后续语句,如图5.1所示。 说明: (1)在循环条件为真的情况下,需要重复执行一个语句 序列,被重复执行的语句序列称为循环体。循环体可以是单 条语句或语句组,语句组是指两条或两条以上的语句,需要用 花括号{}括起来,称为复合语句。 (2)while语句的循环体有可能一次都不会被执行。 while语句 课堂练习 第5 章 循环结构1 03 (3)如果循环条件永远为真,循环将变成无限循环,又称为“死循环”。编程时应避免 陷入“死循环”。 5.1.2 while语句的应用 【例5.1】 计算1~n(含边界值)的所有整数之和,其中n为正整数,由键盘输入。 【问题分析】 要计算1+2+3+…+n的结果,实际上就是多次进行两个数相加的操作(操作次数 与n有关),只是每次相加的被加数和加数不一样。 规划数据结构如下: (1)定义int型变量sum,用于存储求和结果,同时也作为被加数; (2)定义int型变量i,用于存储加数,通过循环,i依次取值1、2、3、…、n。 算法的N-S流程图如图5.2所示。 图5.2 例5.1算法的N-S流程图 【编程实现】 123456789 10 11 12 13 14 #include int main() { int n,i=1,sum=0; printf("请输入一个正整数: "); scanf("%d",&n); while(i<=n) { sum=sum+i; i=i+1; } printf("1 到%d 之间所有整数之和为: %d\n",n,sum); return 0; } 【运行结果】 1 04 C 语言程序设计 【关键知识点】 (1)循环体由两条语句构成,所以需要加{}构成复合语句,如代码第8~11行所示。 (2)代码第10行i=i+1修改了变量i的值,每执行一次循环,变量i的值增加1。这 样,当循环到一定次数的时候,循环条件i<=n不再成立,从而结束循环。 【延展学习】 (1)如果只计算1~n的奇数和,该如何修改程序呢? (2)如果要计算1~n的所有整数的乘积,即求n的阶乘,该如何修改程序呢? (3)如果要计算实数i的n次方,该如何修改程序呢? 【例5.2】 输入一个正整数,求该数的各位数字之和。 【问题分析】 由于不知道输入的正整数有几位,所以只能从个位开始,向高位方向逐一取出各位数 字。以整数2345为例,第一次循环通过2345%10得到2345的个位数5,通过2345/10去 掉最低位得到234;第二次循环通过234%10取出2345的十位数4,通过234/10去掉最 低位得到23;同理,第三次循环得到2345的百位数3和整数2;第四次循环得到2345的 千位数2,且此时2/10变为0,说明已经取完整数所有数位的数字,循环结束。 规划数据结构如下: (1)定义int型变量number,用于存储输入的正整数; (2)定义int型变量sum,用于存储正整数各位数字之和,初始值为0。 算法的N-S流程图如图5.3所示。 图5.3 例5.2算法的N-S流程图 【编程实现】 123456789 10 11 12 13 14 #include int main() { int number,sum=0; printf("请输入一个正整数: "); scanf("%d",&number); while(number>0) { sum=sum+number%10; number=number/10; } printf("该数的各位数字之和为: %d\n",sum); return 0; } 第5 章 循环结构1 05 【运行结果】 【关键知识点】 (1)number%10可以得到正整数number的个位。 (2)number/10可以去掉正整数number的个位。 【延展学习】 (1)如果要得到正整数的反序数(也称逆序数,例如2345的反序数为5432),该如何 编程实现? 分析:以number=2345为例,第1次取出个位数5,进行计算0*10+5得到5后存 入sum 中;第2次取出十位数4,进行计算5*10+4得到54后存入sum 中;第3次取出 百位数3,进行计算54*10+3得到543存入sum 中,以此类推,直到number等于0为 止。因此,将例题代码的第9行修改为sum=sum*10+number%10;即可求出number 的反序数,再适当修改第12行代码中的提示信息即可得到正确的运行结果。 (2)如果要判断某正整数是否为回文数(正读和反读都一样的数,例如1231不是回 文数,1221是回文数),该如何编程实现? 分析:先求出正整数number的反序数sum,再判断原数与反序数是否相等即可。需 要注意的是,当循环结束时,number等于0,原数已不存在,无法判断原数与sum 是否相 等。因此需要在循环开始前,将number的值赋值给变量backup,循环结束后,再判断 backup与sum 是否相等。 5.2 do-while语句 5.2.1 do-while语句 do-while语句用于实现直到型循环,流程图如图5.4所示,语法格式为: do 循环体 while(条件); do-while语句的执行过程:首先执行循环体,然后重复“判断条件-执行循环体”的过 程,直到循环条件不成立为止,退出do-while语句,结束循环,执行do-while语句的后续 语句。说 明: (1)do-while语句的循环体可以是单条语句,也可以是加花括号后构成的复合语句。 (2)while(条件)后面的分号是do-while语句的结束标志,不能省略。 (3)do-while语句的循环体至少执行一次。 do-while语句 课堂练习 1 06 C 语言程序设计 图5.4 do-while语句流程图 5.2.2 do-while语句的应用 【例5.3】 利用莱布尼茨公式π=41-13 +15 -17 +…+(-1)n-1 1 2n-1+… . è . . . ÷ 计算 圆周率,直到最后一项的绝对值小于0.00003时结束运算。 【问题分析】 与例5.1类似,本例也是累和计算,但各累加项(也称为通项)正负交替变化。可以定 义一个变量用来存储1或-1,代表通项的值为正或负,使用该变量乘以通项的值,可实现 通项的正负交替变化。 规划数据结构如下: (1)定义double型变量sum,用于存储累加和(被加数)。 (2)定义double型变量term,用于存储通项(加数)的绝对值。 (3)定义int型变量sign,用于存储实现正负符号的数值1或-1。 算法的N-S流程图如图5.5所示。 图5.5 例5.3算法的N-S流程图 【编程实现】 1234 #include int main() { double sum=0,term=1.0; 求圆周率 微视频 第5 章 循环结构1 07 56789 10 11 12 13 14 15 16 int sign=1,n=1; do { sum=sum+sign*term; n=n+2; term=1.0/n; sign=-sign; }while(term>=3e-5); sum=sum*4; printf("PI=%f\n",sum); return 0; } 【运行结果】 【关键知识点】 (1)第10行term=1.0/n,其中分子1.0是double类型,分母n是int类型,计算时自 动进行类型转换,将n的值转换为double类型,从而得到double类型的除法结果。若写 成term=1/n,则进行的是整除运算,如1/5得0,不是0.2。 (2)第11行,对变量sign进行取负运算,从而实现正负交替变化。 (3)本题的计算结果3.141653,和圆周率的值有较大误差,若需减小误差,可修改循 环条件,例如,将循环条件修改为term>=1e-8,则计算结果为3.141593。 【延展学习】 (1)如果不使用变量sign,而是用if语句处理通项正负符号的问题,即经过判断后, 决定执行sum=sum+term 或sum=sum-term 的操作,该如何修改程序? (2)用沃利斯公式计算圆周率π 2=21 ×23 ×43×45 ×65 ×67 …该如何编程实现? 【例5.4】 编程模拟计算机进行进制转换的过程,即从键盘输入一个十进制整数,将 其转换为二进制输出。 【问题分析】 在1.1.1节中已介绍,十进制整数转换为二进制数的方法是:除以基数倒取余数,直 图5.6 十进制整数转二进 制整数计算过程 到商为0为止。将得到的余数倒序排列(即最后得到的余数是最高位),即得到转换后的 结果,如图5.6所示。 本题的难点是如何将每次得到的余数倒序存储构造成一个 整数,即将图5.6中的余数按公式1×100+0×101+1×102+ 1×103+1×104 =11101 计算后,用int型变量存储转换结 果11101。 规划数据结构如下: (1)定义int型变量n,用于存储输入的十进制整数。 (2)定义int型变量result,初值为0,用于存储转换后的二 1 08 C 语言程序设计 进制整数。 (3)定义int型变量w,表示位权,初值为1,每循环一次,w乘以10,用以表示上述计 算公式中的100、101、102 等。 算法的N-S流程图如图5.7所示。 图5.7 例5.4算法的N-S流程图 【编程实现】 #include int main() { int n,w=1,result=0; printf("请输入一个十进制整数: "); scanf("%d",&n); do { result=result+n%2*w; w=w*10; n=n/2; }while(n!=0); printf("该数转换为二进制是: %d\n",result); return 0; } 【运行结果】 【关键知识点】 在计算机中,图5.6中的转换结果11101实际上是按十进制数存储的,只是用户认为 这是一个二进制数。由于int型变量的最大取值为231 -1=2147483647,因此,变量 result能存储的最大数是1111111111(计算机认为该数是十进制,用户认为该数是二进 制),将二进制的1111111111转换为十进制是1023。因此,本题的程序运行时,输入的十 进制整数最大只能为1023,否则result的值会溢出,得到错误的计算结果。 【延展学习】 (1)将十进制整数转换成八进制数并输出。 第5 章 循环结构1 09 (2)将二进制整数转换成十进制数并输出。二进制转十进制的方法详见1.1.1节。 (3)将八进制整数转换成十进制数并输出。 5.3 for语句 5.3.1 for语句 for语句用于实现当型循环,流程图如图5.8所示,语法格式为: for(表达式1;表达式2;表达式3) 循环体 图5.8 for语句流程图 for语句的执行过程:首先求解表达式1,然后判 断表达式2(即循环条件)是否为真,若为真则执行循 环体,然后求解表达式3;之后重复“判断表达式2→执 行循环体→求解表达式3”的过程,当表达式2的值为 假(即循环条件不成立)时,结束循环,执行for语句的 后续语句。 说明: (1)循环体可以是单条语句,也可以是加花括号 后构成的复合语句。 (2)for语句的圆括号中有且仅有2个分号,用于 将3个表达式分开。表达式1用于为循环控制变量赋 初值;表达式2是循环控制条件;表达式3常用于修正 循环控制变量的值。 例如,以下代码可用于求1+2+3+…+n的和。 int n,i,sum=0; scanf("%d",&n); for(i=1;i<=n;i++) sum=sum+i; (3)当表达式1或表达式3包含多个表达式时,应使用逗号表达式。例如,若n为偶 数,以下代码可求1+2+3+…+n的和;若n为奇数,则求和结果错误(正中间的数会被 加两次)。 int n,i,j,sum; scanf("%d",&n); for(sum=0,i=1,j=n;i<=j;i++,j--) sum=sum+i+j; (4)表达式1、表达式2和表达式3都可以省略,但各表达式间的分号不能省略。 如果省略表达式1,表示无需赋初值(为循环控制变量赋初值可在for语句之前完 for语句课 堂练习 1 10 C 语言程序设计 成);如果省略表达式2,表示循环条件永远为真(在循环体中应有能跳出循环的流程控制 语句,否则会产生死循环);如果省略表达式3,表示没有修正部分(这时应在循环体内修 正循环控制变量的值,以确保循环能正常结束)。 如果同时省略表达式1和表达式3,则for语句等效于while语句,即for(;条件;)等 同于while(条件)。 如果同时省略3个表达式,则循环条件永真,即for(;;)等同于while(1)。 5.3.2 for语句的应用 【例5.5】 计算阶乘之和,即1!+2!+3!+…+n!,n从键盘输入。 【问题分析】 本题可以通过递推法完成。递推法是一种简单的算法,即通过已知条件,利用特定关 系得出中间推论,直至得到最终结果的算法。递推法分为顺推和逆推两种。顺推法是从 已知条件出发,利用特定关系逐步推算出问题的解;逆推法是从问题的结果出发,利用特 定关系逐步推算出问题的开始条件,即顺推法的逆过程。 分析本题中各累加项的关系可知,后一项等于前一项的值乘以项数。 规划数据结构如下: (1)定义int型变量sum,用于存储累加和(被加数),初值为0。 (2)定义int型变量term,用于存储累加项(加数),初值为1,即第1项的值。 (3)定义int型变量i,表示第几项,初值为1。 算法的N-S流程图如图5.9所示。 图5.9 例5.5算法的N-S流程图 【编程实现】 123456789 10 #include int main() { int sum=0,term=1,i,n; printf("请输入一个正整数: "); scanf("%d",&n); for(i=1;i<=n;i++) { term=term*i; sum=sum+term; 第5 章 循环结构1 11 11 12 13 14 } printf("sum=%d\n",sum); return 0; } 【运行结果】 【关键知识点】 (1)若代码第4行未给变量赋初值,则可将第7行修改为for(sum=0,term=1,i= 1;i<=n;i++),即在for语句的表达式1中使用逗号表达式为多个变量赋初值。 (2)第9行实现了顺推法,即第1次循环时计算1×1得到第1个累加项的值,存入 term 中。第2次循环时计算1×2得到第2个累加项的值,存入term 中。以此类推,每 次循环都用前一项的值乘以i得到当前项的值。 【延展学习】 (1)除了用递推法之外,本题能否用其他方法编程实现? (2)当n超过12后,运行结果不正确,其原因是13!=6227020800,已超出int型变 量的数据表示范围,所以sum 和term 的值会溢出。请思考变量sum 和term 应定义为什 么类型? 第12行的输出语句应如何修改? 【例5.6】 编程输出所有的水仙花数。水仙花数是一个三位数,其各位数字的立方之 和等于该数本身,例如,153=13+53+33,因此153是水仙花数。 【问题分析】 枚举法是利用计算机运算速度快、精确度高的特点,对要解决问题的所有可能情况, 一个不漏地进行检验,从中找出符合要求的答案,因此枚举法是通过牺牲时间来换取答案 的全面性。 本题可采用枚举法,即定义int型变量i,用for循环控制i的值从100变化到999,对 于每一个i,取出它的个位、十位、百位数字,并用if语句判断它是否满足水仙花数的 条件。 【编程实现】 123456789 10 11 #include int main() { int i,a,b,c; printf("水仙花数有: "); for(i=100;i<=999;i++) { a=i/100; //百位数字 b=i/10%10; //十位数字 c=i%10; //个位数字 if(i==a*a*a+b*b*b+c*c*c)