循环结构程序设计 5.1 程序中需要用循环结构 用顺序结构和选择结构可以解决简单的、不出现重复的问题。但是在现实生活中许多 问题是需要进行重复处理的。例如,计算一个学生5门课的平均成绩很简单,只需要把5门 课的成绩相加,然后除以5即可。如果需要得到一个班50 个学生每人的平均成绩,就要做 50 次“把5门课的成绩相加,然后除以5的(”) 工作,如果在程序中重复写50 次相同的程序段 显然是不胜其烦的。类似的问题是很多的,如工厂各车间的生产日报表、全国各省市的人口 统计分析、各大学招生情况统计、全校教职工工资报表等。 事实上,绝大多数的应用程序都包含重复处理。循环结构就是用来处理需要重复处理 的问题的,所以。循环结构又称为重复结构。 有两种循环:一种是无休止的循环,如地球围绕太阳旋转,永不终止;每一天24 小时, 周而复始。另一种是有终止的循环,达到一定条件循环就结束了,如统计完第50 名学生成 绩后就不再继续了。计算机程序只处理有条件的循环,算法的特性是有效性、确定性和有穷 性,如果程序永远不结束,是不正常的。 要构成一个有效的循环,应当指定两个条件:(1)需要重复执行的操作,这称为循环体; (2)循环结束的条件,即在什么情况下停止重复的操作。 循环结构是结构化程序设计的基本结构之一,它和顺序结构、选择结构共同作为各种复 杂程序的基本构造单元。因此熟练掌握选择结构和循环结构的概念及使用是程序设计的最 基本的要求。 C语言提供了几种能直接实现循环结构的语句,主要有while语句、do…while语句和 for语句,用起来很方便。下面分别介绍。 5.2 用while语句和do…while语句实现循环 5.2.1 用while 语句实现循环 先看一下利用循环的例子。 1 31 例5.1 求1+2+3+…+100,即Σ100 n=1 n。 解题思路: 对此问题可以有不同的求解方法,有的人用心算,把它化成50组头尾两数之和:(1+ 100)+(2+99)+(3+98)+…+(49+52)+(50+51),每个括号内的值都是101,一共有50 对括号,所以总和是50×101,很容易得出5050。这是适宜于心算的算法。 用计算机算题,计算机是不会按上面的方法自动分组的,而必须事先由人们设计计算的 方法。对于这样简单的问题去设计巧妙的算法是没有必要的。计算机的最大特点是快,所 以适宜用最“笨”的办法去处理一些简单的问题,就是采取一个一个数累加的方法,从1加到 100。对于人来说,这是“笨”办法,对于计算机来说却是“好”办法。 用传统流程图和N-S结构流程图表示从1加到100的算法,见图5.1(a)和图5.1(b)。 图 5.1 其思路是:变量sum 是用来存放累加值的,sum 的 初值设为0,i是准备加到sum 的数值,让i从1变 到100,先后累加到sum 中。具体步骤如下: (1)开始时使sum 的值为0,被加数i第一次 取值为1。开始进入循环结构。 (2)判别“i≤100”条件是否满足,由于i小于 100,因此“i≤100”的值为真。所以应当执行其下面 矩形框中的操作。 (3)执行sum=sum+i,此时sum 的值变为1 了,然后使i的值加1,i的值变为2了,这是为下一 次加2作准备。流程返回菱形框。 (4)再次检查“i≤100”条件是否满足,由于i的值为2,小于100,因此“i≤100”的值仍为 真,所以应执行其下面矩形框中的操作。 (5)执行sum=sum+i,由于sum 的值已变为1,i的值已变为2,因此执行sum=sum +i后sum 的值变为3。再使i的值加1,i的值变为3。流程再返回菱形框。 (6)再次检查“i≤100”条件是否满足……如此反复执行矩形框中的操作,直到i的值变成 了100,把i加到sum中,然后i又加1变成101了。当再次返回菱形框检查“i≤100”条件时,由 于i已是101,大于100,“i≤100”的值为假,不再执行矩形框中的操作,循环结构结束。 编写程序: #include int main() {int i,sum=0; //sum 是用来存放累加和的变量,初值为0 i=1; while (i<=100) //当i 小于或等于100 时,执行下面大括号中的复合语句 1 32 { sum=sum+i; //将i 的当前值累加到变量sum 中 i++; //使i 的值加1 }p rintf("%d\n",sum); return 0; } 运行结果: 5050 从上面的程序可以看到怎样用while语句去实现循环。while语句的一般形式如下: while (表达式) 语句 当表达式为非0值(代表逻辑值“真”)时,执行while语句中的内嵌语句(如程序中大括 号中的复合语句),其流程图见图5.2。 图 5.2 while循环的特点是:先判断表达式,后执行循环体(即内嵌语句)。 注意: (1)循环体如果包含一个以上的语句,应该用大括号括起来,以复合语句形式出现。 如果不加大括号,则while语句的范围只到while后面第一个分号处。例如,本例中while 语句中如无大括号,则while语句范围只到“sum=sum+i;”。 (2)在循环体中应有使循环趋向于结束的语句。例如,在本例中循环结束的条件是 “i>100”,因此在循环体中应该有使i增值以最终导致i>100的语句,现用“i++;”语句 来达到此目的。如果无此语句,则i的值始终不改变,循环永不结束。 请读者考虑:如果while语句中的条件改为“i<100”,情况会怎样? 输出结果是 什么? 1 33 5.2.2 用do…while 语句实现循环 do…while语句的特点是先执行循环体,然后判断循环条件是否成立。其一般形式为: do 循环体语句 while (表达式); 它是这样执行的:先执行一次循环体语句,然后判别“表达式”,当表达式的值为非0(真) 时,返回重新执行循环体语句,如此反复,直到表达式的值等于0(假)为止,此时循环结束。可 以用图5.3表示其流程。请注意do…while循环用N-S流程图的表示形式(见图5.3(b))。 图 5.3 同一个问题既可以用while循环处理,也可以用do…while循环来处理。二者是可以互 相转换的。 例5.2 用do…while循环求1+2+3+…+100,即Σ100 n=1 n。 解题思路: 画出流程图,见图5.4。 图 5.4 1 34 编写程序: #include int main() { int i,sum=0; i=1; do //在循环开始时不检查条件,先执行一次循环体 {sum=sum+i; i++; }while(i<=100); printf("%d\n",sum); return 0; } 运行结果: 5050 可以看到,结果和例5.1完全相同。 例5.3 若要募集慈善基金10000元,有若干人捐款,每输入一个人的捐款数后,计算 机就输出当时的捐款总和。当某一次输入捐款数后,总和达到或超过10000元时,即宣告结 束,输出最后的累加值。 解题思路: 解此题的思路是设计一个循环结构,在其中输入捐款数,求出累加值,然后检查此时的 累加值是否达到或超过预定值,如果达到了,就结束循环操作。 编写程序: #include int main() {float amount,sum=0; //变量sum 用来存放累加和 do {scanf("%f",&amount); //输入一个捐款金额 sum=sum+amount; //求出当前的累加和 }while(sum<10000); //如未达10000 元继续循环 printf("sum=%9.2f\n",sum); return 0; } 1 35 运行结果: 1000 ↙ (输入捐款额) 1850 ↙ 1500 ↙ 2600 ↙ 2500 ↙ 1200 ↙ sum=10650.00 程序分析: 此题与前面的不同,事先不知道要执行多少次循环,只给出循环的条件(sum<10000),每 次循环结束时检查此条件是否满足,当某一次sum已超过10000元时,不再继续执行循环体。 提示:设计循环结构,要考虑两个问题:一是循环体,二是循环结束条件。注意while 循环中判断的条件是循环继续的条件,而不是结束条件。在上例中循环继续的条件是 sum<10000,也就是结束循环的条件是sum>=10000。千万不要错写成while(sum> 10000)。 5.3 用for语句实现循环 用while语句可以实现循环结构,但是它必须明确地给出继续执行循环的条件(如sum <10000),而在许多情况下,人们给出的往往是执行循环的次数,如统计100人的平均工资, 求一个学生5门课的总成绩等。用C语言中的for语句更为灵活方便,不仅可以用于循环 次数已经确定的情况,而且可以用于循环次数不确定而只给出循环结束条件的情况,它完全 可以代替while语句。 5.3.1 for 语句的一般形式和执行过程 for语句的一般形式为 for(表达式1;表达式2;表达式3) 语句 它的执行过程如下: (1)先求解表达式1。 (2)求解表达式2,若其值为真(值为非0),则执行for语句中指定的内嵌语句,然后执 行下面第(3)步。若为假(值为0),则结束循环,转到第(5)步。 (3)求解表达式3。 1 36 (4)转回第(2)步继续执行。 (5)循环结束,执行for语句下面的一个语句。 可以用图5.5来表示for语句的执行过程。 图 5.5 for语句最简单的应用形式也就是最易理解的如下形式: for(循环变量赋初值;循环条件;循环变量增值) 语句 例如: for(i=1;i<=100;i++) sum=sum+i; 的执行过程与图5.1完全一样。它相当于以下语句: i=1; while(i<=100) { sum=sum+i; i++; } 显然,用for语句简单、方便。 for循环语句功能丰富,使用灵活,方法多变,使用上有许多技巧,可以参阅本章的提高 部分。 5.3.2 for 循环程序举例 学习了循环以后,可以实现一些有趣的算法。 例5.4 国王的小麦。相传古代印度国王舍罕要褒赏聪明能干的宰相达依尔(国际象 1 37 棋的发明者),国王问他要什么? 达依尔回答说:“国王只要在国际象棋的棋盘第1个格子 中放1粒麦子,第2个格子中放2粒麦子,第3个格子中放4粒麦子,以后按此比例每一格 加一倍,一直放到第64格(国际象棋的棋盘是8×8=64格),我感恩不尽,其他什么都不要 了。”国王想:这有多少! 还不容易! 让人扛来一袋小麦,但不到一会儿全用没了,再来一袋 很快又用完了。结果全印度的粮食全部用完还不够。国王纳闷,怎样也算不清这笔账。现 在用计算机来计算一下。 解题思路: 每个格子中的麦子粒数见图5.6。 图 5.6 麦子的总粒数是: 1+2+22 +23 + … +263 分别计算出每一格的麦子粒数,把它们加起来,就得到总粒数。据估算,1立方米的小 麦约有1.42×108 粒,由此可以大致计算出小麦的体积。 可以用for语句实现循环。画出流程图(见图5.7),其中图5.7(a)是N-S流程图,图5.7(b)是 传统流程图。 编写程序: #include int main() {double p=1, t=1, v; int i; for(i=1; i<64; i++) //执行63 次循环 { p=p * 2; //p 是当前一个格子中的麦子粒数 t=t+p; //t 是当前麦子总粒数 } v=t/1.42e8; //v 是总体积,单位为立方米 1 38 printf("total=%e\n",t); //用指数形式输出麦子总粒数 printf("volume=%e\n",v); //用指数形式输出麦子总体积 return 0; } 图 5.7 运行结果: total=1.844674e+019 volume=1.299066e+011 计算结果为:共有小麦约1.844674×1019粒,体积约1.3×1011m3。相当于全中国960 万平方千米的土地上,全铺满1.3cm 厚的小麦,相当我国几百年的产量。 程序分析: 变量p用来存放一个格子中的麦子粒数,变量t用来存放某一时刻的麦子总粒数,变量 v用来存放麦子的体积。变量i用来控制循环的次数,开始时i=1,开始第1次循环,得到的 p值是第2格的麦子粒数(请思考为什么),t是前2格的麦子总粒数。在完成第1次循环 后,i的值加1变为2,由于2<64,所以执行第2次循环,此时得到的p值是第3格的麦子粒 数,t是前3格的麦子总粒数。依此类推,当i变到63时,执行最后一次循环,此时得到的p 值是第64格的麦子粒数,t是64格的麦子总粒数。i再变为64,由于i不再小于64了,不再 1 39 执行循环。接着计算体积,输出结果。 请读者分析: (1)程序执行了63次循环,那么怎样实现累加了64个格子的小麦呢? (2)如果把第5行改为:for(i=1;i<=64;i++),结果会怎样? (3)如果把第5行改为:for(i=0;i<64;i++),结果会怎样? 不妨上机试验一下。 例5.5 人口增长预测。据2012年末统计,我国人口约为13.54亿人,如果人口的年增 长率为1%,请计算到哪一年中国总人口超过15亿。 解题思路: 计算人口增长和计算存款利息的公式是相同的。假设原来人口为p0,一年后的人口 为p: p =p0 × (1+r) 其中r 是年增长率。用此公式依次计算出每年的人口,每算出一年的人口后就检查一下是 否达到或超过15亿。如果未达到或超过15亿,就再计算下一年的人口,直到某一年的人口 达到或超过15亿为止。 编写程序: #include int main() {double p=1.354e9,r=0.01; int year; for(year=2012; p<1.5e9; year++) { p=p*(1+r); //赋值号两侧的变量p 代表不同含义 } printf("year=%d,p=%e\n",year-1,p); return 0; } 运行结果: year=2023,p=1.510615e+009 即到2023年,中国人口达到15.10615亿。如果增长率r 改为0.5%(即0.005),则结果为: year=2033,p=1.503509e+009 即到2033 年,中国人口达到15.03509 亿 程序分析: 程序中没有用两个变量p0和p来代表原来人口和一年后的人口,而用一个变量p代表