第5章循环结构程序设计〖1〗5.1为什么需要循环控制前面介绍了程序中常用到的顺序结构和选择结构,但是只有这两种结构是不够的,还需要用到循环结构(或称重复结构)。因为在日常生活中或是在程序所处理的问题中常常遇到需要重复处理的问题。例如:  要向计算机输入全班50个学生的成绩;(重复50次相同的输入操作)  分别统计全班50个学生的平均成绩;(重复50次相同的计算操作)  求30个整数之和;(重复30次相同的加法操作)  检查30个学生的成绩是否及格。 (重复30次相同的判别操作) 要处理以上问题,最原始的方法是分别编写若干个相同或相似的语句或程序段进行处理。例如为了统计全班50个学生的平均成绩,可以先编写求一个学生平均成绩的程序段: scanf(″%f,%f,%f,%f,%f″,&score1,&score2,&score3,&score4,&score5);//输入一个学生5门课的成绩 aver=(score1+score2+score3+score4+score5)/5;//求该学生平均成绩 printf(″aver=%7.2f″,aver);//输出该学生平均成绩然后再重复写49个同样的程序段。这种方法虽然可以实现要求,但是显然是不可取的,因为工作量大,程序冗长、重复,难以阅读和维护。相信每一位读者都会认为这是最笨的办法。实际上,几乎每一种计算机高级语言都提供了循环控制,用来处理需要进行的重复操作。 在C语言中,可以用循环语句来处理上面的问题: i=1;//设整型变量i初值为1 while(i<=50) //当i的值小于或等于50时执行花括号内的语句 {scanf(″%f,%f,%f,%f,%f″,&score1,&score2,&score3,&score4,&score5); //输入一个学生5门课的成绩 aver=(score1+score2+score3+score4+score5)/5; //求该学生平均成绩 printf(″aver=%7.2f″,aver);//输出该学生平均成绩 i++;//每执行完一次循环使i的值加1 } 可以看到: 用一个循环语句(while语句),就把需要重复执行50次程序段的问题解决了。一个while语句实现了一个循环结构。请读者先阅读这个程序段,理解循环结构的执行过程,在下一节将对其执行过程作必要的说明。 大多数的应用程序都会包含循环结构。循环结构和顺序结构、选择结构是结构化程序设计的3种基本结构,它们是各种复杂程序的基本构成单元。因此熟练掌握选择结构和循环结构的概念及使用是进行程序设计最基本的要求。 5.2用while语句实现循环 在5.1节中已看到了在C程序中可以用while语句来实现循环结构。上面的while循环结构就是一个while语句,它的执行过程是: 开始时变量i的值为1,while语句首先检查变量i的值是否小于或等于50,如果是,则执行while后面的语句(称为循环体,在本例中是花括号内的复合语句)。在循环体中先输入第1个学生5门课的成绩,然后求出该学生的平均成绩aver,并输出此平均成绩。请思考最后一行“i++;”的作用。图5.1 它使i的值加1,i的原值为1,现在变成2了。然后流程返回到while语句的开头,再检查i的值是否小于或等于50,由于i的值2小于50,因此又执行循环体,输入第2个学生5门课的成绩,然后求出第2个学生的平均成绩并输出此平均成绩。i++又使变量i的值变为3,处理第3个学生的数据……直到处理完第50个学生的数据后,i的值变为51。由于它大于50,因此不再执行循环体。流程图见图5.1,其中,虚线框内为while循环结构。 while语句的一般形式如下: while (表达式) 语句 其中的“语句”就是循环体。循环体只能是一个语句,可以是一个简单的语句,还可以是复合语句(用花括号括起来的若干语句)。执行循环体的次数是由循环条件控制的,这个循环条件就是上面一般形式中的“表达式”,它也称为循环条件表达式。当此表达式的值为“真” (以非0值表示)时,就执行循环体语句;为“假” (以0表示)时,就不执行循环体语句。例如“i<=50”是一个循环条件表达式,它是一个关系表达式。它的值只能是“真”或“假”。在执行while语句时,先检查循环条件表达式的值,当为非0值(真)时,就执行while语句中的循环体语句;当表达式为0(假)时,不执行循环体语句。其流程图见图5.2。 while语句可简单地记为: 只要当循环条件表达式为真(即给定的条件成立),就执行循环体语句。 注意: while循环的特点是先判断条件表达式,后执行循环体语句。 通过下面的例子,可以学习到怎样利用while语句进行循环程序设计。 【例5.1】求1+2+3+…+100,即∑100n=1n。 解题思路: 在处理这个问题时,先分析此题的特点: (1) 这是一个累加的问题,需要先后将100个数相加。要重复进行100次加法运算,显然可以用循环结构来实现。重复执行循环体100次,每次加一个数。 (2) 分析每次所加的数有无规律。发现每次累加的数是有规律的,后一个数是前一个数加1。因此不需要每次用scanf语句从键盘临时输入数据,只须在加完上一个数i后,使i加1就可得到下一个数。 为了使思路清晰,画出传统流程图和NS结构流程图表示算法,见图5.3。 图5.2 图5.3 编写程序。根据流程图写出程序: #include int main() { int i=1,sum=0;//定义变量i的初值为1,sum的初值为0 while(i<=100)//当i>100,条件表达式i<=100的值为假,不执行循环体 {//循环体开始 sum=sum+i;//第1次累加后,sum的值为1 i++;//加完后,i的值加1,为下次累加做准备 }//循环体结束 printf(″sum=%d\\n″,sum);//输出1+2+3…+100的累加和 return 0; }运行结果: 程序分析: (1) 循环体如果包含一个以上的语句,应该用花括号括起来,作为复合语句出现。如果不加花括号,则while语句的范围只到while后面第1个分号处。例如,本例中while语句中如无花括号,则while语句范围只到“sum=sum+i;”为止。 (2) 不要忽略给i和sum赋初值(这是未进行累加前的初始情况),否则它们的值是不可预测的,结果显然不正确。读者可上机试一下。 (3) 在循环体中应有使循环趋向于结束的语句。例如,在本例中循环结束的条件是“i>100”,因此在循环体中应该有使i增值以最终导致i>100的语句,本例用“i++;”语句来达到此目的。如果无此语句,则i的值始终不改变,循环永远不结束。 5.3用do…while语句实现循环 除了while语句以外,C语言还提供了do…while语句来实现循环结构。如: int i=1;//设变量i的初值为1 do//循环结构开始 { printf(″%d″,i++);//循环体,输出i的值,然后使i加1 } while(i<=100);//当i小于或等于100时,继续执行循环体它的作用是: 执行(用do表示“做”)printf语句,然后在while后面的括号内的表达式中检查i的值,当i小于或等于100时,就返回再执行一次循环体(printf语句),直到i大于100为止。执行此do…while语句的结果是输出1~100,共100个数。请注意分析printf函数中的输出项i++的作用: 先输出当前i的值,然后再使i的值加1。如果改为printf(″%d″,++i),则是先使i的值加1,然后输出i的新值。若在执行printf函数之前,i的值为1,则printf函数的输出是i的新值2。在本例中do下面的一对花括号其实不是必要的,因为花括号内只有一个语句。可以写成do printf(″%d″,i++); while(i<=100);但这样写,容易使人在看到第2行末尾的分号后误认为整个语句结束了。为了使程序清晰、易读,建议把循环体用花括号括起来。 do…while语句的执行过程是: 先执行循环体,然后再检查条件是否成立,若成立,再执行循环体。这是和while语句的不同。 注意: do…while语句的特点是,先无条件地执行循环体,然后判断循环条件是否成立。 do…while语句的一般形式为 do 语句 while (表达式); 其中的“语句”就是循环体。它的执行过程可以用图5.4表示。请注意do…while循环用NS流程图的表示形式(图5.4(b))。 先执行一次指定的循环体语句,然后判别表达式,当表达式的值为非零(“真”) 时,返回重新执行循环体语句,如此反复,直到表达式的值等于0(“假”)为止,此时循环结束。 【例5.2】用do…while语句求1+2+3+…+100,即∑100n=1n。 解题思路: 与例5.1相似,用循环结构来处理。但题目要求用do…while语句来实现循环结构。先画出流程图,见图5.5。图5.4 图5.5 编写程序。根据流程图可以很容易写出以下程序: #include int main() { int i=1,sum=0; do { sum=sum+i; i++; }while(i<=100); printf(″sum=%d\\n″,sum); return 0; }运行结果: 程序分析: 从例5.1和例5.2可以看到: 对同一个问题可以用while语句处理,也可以用do…while语句处理。do…while语句结构可以转换成while结构。如图5.4可以改画成图5.6形式,二者完全等价。图5.6而图5.6中虚线框部分就是一个while结构。可见,do…while结构是由一个“语句”加一个while结构构成的。若图5.2中表达式值为真,则图5.2也与图5.6等价(因为都要先执行一次“语句”)。 在一般情况下,用while语句和用do…while语句处理同一问题时,若二者的循环体部分是一样的,那么结果也一样。如例5.1和例5.2程序中的循环体是相同的,得到的结果也相同。但是如果while后面的表达式一开始就为假(0值)时,两种循环的结果是不同的。【例5.3】while和do…while循环的比较。 (1) 用while循环:#include int main() { int i,sum=0; printf(″please enter i,i=?″); scanf(″%d″,&i); while(i<=10) { sum=sum+i; i++; } printf(″sum=%d\\n″,sum); return 0; }运行结果(两次): (2) 用do…while循环:#include int main() { int i,sum=0; printf(″please enter i,i=?″); scanf(″%d″,&i); do { sum=sum+i; i++; }while(i<=10); printf(″sum=%d\\n″,sum); return 0; }运行结果(两次): 再运行一次: 可以看到,当输入i的值小于或等于10时,二者得到的结果相同;而当i>10时,二者结果就不同了。这是因为此时对while循环来说,一次也不执行循环体(表达式“i<=10”的值为假),而对do…while循环语句来说则至少要执行一次循环体。可以得到结论: 当while后面的表达式的第1次的值为“真”时,两种循环得到的结果相同;否则,二者结果不相同(指二者具有相同的循环体的情况)。 5.4用for 语句实现循环 除了可以用while语句和do…while语句实现循环外,C语言还提供了for语句实现循环,而且for语句更为灵活,不仅可以用于循环次数已经确定的情况,还可以用于循环次数不确定而只给出循环结束条件的情况,它完全可以代替while语句。 例如: for (i=1;i<=100;i++)//控制循环次数,i由1变到100,共循环100次 printf(″%d″,i);//执行循环体,输出i的当前值它的执行过程见图5.7。图5.7 它的作用是: 输出1~100,共100个整数。 for语句的一般形式为 for(表达式1;表达式2;表达式3) 语句 括号中3个表达式的主要作用是: 表达式1: 设置初始条件,只执行一次。可以为零个、一个或多个变量设置初值(如i=1)。 表达式2: 是循环条件表达式,用来判定是否继续循环。在每次执行循环体前先执行此表达式,决定是否继续执行循环。 表达式3: 作为循环的调整,例如使循环变量增值,它是在执行完循环体后才进行的。 最常用的for语句形式是: for (循环变量赋初值;循环条件;循环变量增值) 语句 例如:for(i=1;i<=100;i++) sum=sum+i;其中的“i=1”是给循环变量i设置初值为1,“i<=100”是指定循环条件: 当循环变量i的值小于或等于100时,循环继续执行。“i++”的作用是使循环变量i的值不断变化,以便最终满足终止循环的条件,使循环结束。也就是: 循环变量i的初值为1,循环变量增量为1,循环变量终值为100,每执行一次循环,i的值加1,直到i的值大于100,就不再执行了。 for语句的执行过程如下: (1) 求解表达式1。本例中把整数1赋给变量i。 (2) 求解表达式2,若此条件表达式的值为真(非0),则执行for语句中的循环体,然后执行第(3)步。若为假(0),则结束循环,转到第(5)步。 上例中,循环条件表达式“i<=100”是一个关系表达式,当i=1时,表达式i<=100的值为真(非0),故执行循环体中的语句,即printf语句,输出i的当前值1。然后执行第(3)步。 (3) 求解表达式3。在本例中,执行i++,使i的值加1,i的值变成2。 (4) 转回步骤(2)继续执行。 由于此时i=2,表达式i<=100的值为真,再次执行循环体中的语句,printf语句输出i的当前值2。然后再执行步骤(3)。如此反复,直到i变到101,此时表达式i<=100的值为假,不再执行循环体,而转到步骤(5)。图5.8 可以用图5.8来表示for语句的执行过程。 注意: 在执行完循环体后,循环变量的值“超过”循环终值,循环结束。例如,在本例中,在执行完循环体后循环变量i的值为101,大于循环终值100。如果循环变量的增值为负值,如: for(i=100;i>=1;i--),执行完循环体后循环变量i的值为0,小于循环终值1。其规律为: 循环变量沿着变化的方向“超过”循环终值,循环就结束了。 (5) 循环结束,执行for语句下面的一个语句。 上面看到的for语句for(i=1;i<=100;i++) sum=sum+i;其执行过程与图5.3完全一样。它相当于以下语句: i=1; while(i<=100) { sum=sum+i; i++; }显然,用for语句简单、方便。 说明: (1) for语句的一般形式如下: for(表达式1;表达式2;表达式3) 语句 可以改写为while循环的形式: 表达式1; while 表达式2 { 语句 表达式3 } 二者无条件等价。(2) “表达式1”可以省略,即不设置初值,但表达式1后的分号不能省略。例如: for(;i<=100;i++) sum=sum+i; //for语句中没有表达式1应当注意: 由于省略了表达式1,没有对循环变量赋初值,因此,为了能正常执行循环,应在for语句之前给循环变量赋以初值。即i=1;//对循环变量i赋初值 for(;i<=100;i++) sum=sum+i; //for语句中没有表达式1执行for语句时,跳过图5.8中的“求解表达式1”这一步。由于在for语句前加了“i=1;”,因此其作用仍然不变。 (3) 表达式2也可以省略,即不用表达式2来作为循环条件表达式,不设置和检查循环的条件。如: 图5.9 for(i=1; ;i++) sum=sum+i;此时循环无终止地进行下去,也就是认为表达式2始终为真,见图5.9。 它相当于i=1; while(1) { sum=sum+i; i++; }循环无终止地进行,i的值不断加大,sum的值也不断累加。 (4) 表达式3也可以省略,但此时程序设计者应另外设法保证循环能正常结束。例如: for(i=1;i<=100;)//没有表达式3 { sum=sum+i; i++;//这时可以在循环体中使循环变量增值 }在上面的for语句中只有表达式1和表达式2,而没有表达式3。i++的操作不放在表达式3的位置,而作为循环体的一部分,效果是一样的,都能使循环正常结束。如果在循环体中无此“i++;”语句,则循环体无止境地执行下去。 (5) 如果表达式1和表达式3都没有,只有表达式2,即只给循环条件,情况会怎样?如: for(;i<=100;)//没有表达式1和表达式3,只有表达式2 { sum=sum+i; i++;//在循环体中使循环变量增值 }当然,应当在for语句前给循环变量赋初值,否则循环无法正常执行。即: i=1;//给循环变量赋初值 for(;i<=100;)//没有表达式1和表达式3,只有表达式2 { sum=sum+i; i++; //在循环体中使循环变量增值 }相当于 i=1; while(i<=100) { sum=sum+i; i++; }可见for语句比while语句功能强,除了可以给出循环条件外,还可以赋初值,使循环变量自动增值等。 (6) 甚至可以将3个表达式都可省略,例如: for(; ;) printf(″%d\\n″,i);相当于while(1) printf(″%d\\n″,i);即不设初值,不判断条件(认为表达式2为真值),循环变量也不增值,无终止地执行循环体语句,显然这是没有实用价值的。 (7) 表达式1可以是设置循环变量初值的赋值表达式,也可以是与循环变量无关的其他表达式。例如:for (sum=0;i<=100;i++) sum=sum+i;表达式3也可以是与循环控制无关的任意表达式。但不论怎样写for语句,都必须使循环能正常执行。 图5.10(8) 表达式1和表达式3可以是一个简单的表达式,也可以是逗号表达式,即包含一个以上的简单表达式,中间用逗号间隔。如: for(sum=0,i=1;i<=100;i++) sum=sum+i;或for(i=0,j=100;i<=j;i++,j--) k=i+j;表达式1和表达式3都是逗号表达式,各包含两个赋值表达式,即同时设两个初值(i=0,j=100),使两个变量增值(i++,j--),执行情况见图5.10。 在逗号表达式内按自左至右顺序求解,整个逗号表达式的值为最右边的表达式的值。例如:for(i=1;i<=100;i++,i++) sum=sum+i;相当于