第5章 迭代计算与循环结 构 5.1 概述 利用分支语句或条件表达式可以实现从两个或3个数中找出最大数。但是当有更多 的数据(例如50 个数据或500 个数据)时,如何找到其中的最大数呢? 下面是一个最基本 的解决思路。 步骤1:对第一个数,假设它可能是最大的数,将其标记为当前最大数。 步骤2:对下一个数,如果比标记的数大,则标记该数为当前最大数,否则什么都 不做。 步骤3:重复步骤2,直到处理完所有的数。 步骤4:当前标记的数就是要找出的最大数。 其中,步骤2多次重复操作。程序设计将此类多次重复处理的逻辑结构称为循环结 构,并引入循环语句实现循环结构。 循环结构一般可以表述为“当条件成立时,反复执行特定的操作序列,如果条件不 成立则结束循环”(称为前判断结构、当型循环), 或者表述为“执行特定的操作序列,如 果条件成立,反复执行操作序列,直到条件不再成立时结束循环”(称为后判断结构、 直到型循环)。其中的“条件”称为循环条件,可以为关系表达式、逻辑表达式及其他 有确定值的表达式。条件作为一个判断结果,只要是非0值,其逻辑值就为真,否则 为假。“操作序列”称为循环体,循环体作为一个顺序执行的操作序列,可以是空语 句、表达式语句、块语句、选择结构语句或循环结构语句。在循环体执行过程中,如 果不能改变循环条件的值,循环将永远执行,从而失去循环处理的意义。所以,循环 体中通过修改与循环条件相关的变量值来改变循环条件,该变量称为循环控制 变量。 当型循环结构的特点是先判断循环条件,循环体可能执行多次,也有可能一次也不执 行;直到型循环结构的特点是先执行一次循环体,再判断循环条件,循环体至少会被执 行一次。C语言提供的循环语句包括while、for和do…while。在某些特殊情况下,还可 以通过break、continue、goto语句等C语言语句中断循环,直接改变循环迭代的执行 过程。 1 04 程序设计基础(C 语言)(第4 版) 5.2 while语句 while语句实现当型循环,先判断循环条件,后执行循环体语句。循环体可能一次也 不执行。 语句格式: while (循环条件表达式) { 语句块; } 图5-1 while语句流程 while为关键字,语句块为循环体。执行流程如图5-1所 示。检查循环条件表达式,如果表达式值为真,执行循环体;否 则,循环结束。 一般不能事先确定while语句控制循环的次数,需要根据 循环条件判定,如果开始的循环条件为假,则循环体一次也不 执行。由 多条语句构成的循环体务必放置在“{}”中,否则将导致 逻辑上的错误。 例5-1 编写一个程序,输出1~100的自然数之和。 #include<stdio.h> int main() { int i, sum=0; i=1; while (i<=100) /*循环控制条件i<=100*/ { sum=sum+i; /*累加和,还可以写成sum+=i;*/ i++; /*循环控制变量改变i++*/ }p rintf("%d\n",sum); return 0; } 这是一个累加求和问题。声明int型变量sum 用于存储计算自然数总和的值,其初 始值设置为0。如果没有初始化,声明sum 时为其分配的存储空间内为随机值,该值将作 为初始值参与求和计算从而导致计算结果错误。声明int型的循环控制变量i并初始化 为1,循环体内通过i值的自增将1,2,…,100加入到sum 中。while语句是否继续循环 执行的条件,即判断循环控制变量i的值是否小于或等于100。i+ + 实现i从1至100的 变化,每次变化满足i小于或等于100的循环条件,则执行循环体,并将当前变量i值累加 到变量sum 中。当i大于100(此时i的值为101)时,循环条件i< =100为假,循环终止, 程序转而执行循环体后面的语句。 决定循环执行次数的因素包括循环条件、循环控制变量的初值和终值、循环控制变量 每次变化的幅度等。循环控制变量每次增加或减少的值称为步长。如果循环控制变量具 第5 章 迭代计算与循环结构1 05 有固定的步长,则循环次数的计算公式如下: 循环次数=(终值-初值)/步长+1 例5-1中循环控制变量i从1循环到100,步长为1,循环次数为(100-1)/1+1=100 次。步长也可以是负数,改写例5-1为如下代码可得到同样的结果: #include<stdio.h> int main() { int i,sum=0; i=100; /*初始值为100*/ while (i>=1) /*循环控制条件i>=1*/ { sum=sum+i; /*循环控制变量i 参与求和计算*/ i--; /*循环控制变量自减*/ }p rintf("%d\n",sum); return 0; } 如果循环体只有一条语句,可以省略包含循环体的花括号“{}”。常用的while语句 有两种简单表示形式。 (1)循环体为空语句。例如: i=0; while (++i<10); /*判断循环条件是否成立的同时计算循环控制变量i,即i 先自增(++1) 再判断是否满足条件i<10。若满足条件执行空语句;当++i 为10 时, i=10,循环条件不再成立,结束循环*/ (2)循环体为表达式语句。例如: i=0; while (i<10) ++i; /*当条件i<10 成立时,执行循环体+ + i,直到i= 10,循环条件不再成立, 结束循环*/ 但是仍然建议初学时,即使循环体只有一条语句也使用花括号包含该语句。例如: int i=1; while (i<=10) { sum=sum+i++; /*循环控制变量i 参与求和计算*/ } 此外,while(1)表示循环条件为逻辑真,构成一个无限循环。 例5-2 编写程序,输入10个整数,输出其中最大的数。 #include<stdio.h> int main() { int a,i,max; scanf("%d",&a); max=a; /*假设第一次输入的数为当前最大数*/ i=1; while (i<10) { scanf("%d",&a); if (max<a) 1 06 程序设计基础(C 语言)(第4 版) max=a; /*第i 次输入的数为当前最大数*/ i++; }p rintf("最大的数是%d\n",max); return 0; } 首先,输入一个数并假设它为当前最大数,存储在max中;其次,依次输入其他9个 数,对于每一次输入,都与之前输入数据中的最大值max进行比较,如果大于max,则将 此次输入数据存储于max中;循环执行结束后max中存储的即所有数中的最大数。如 果需要找出50个数中的最大数,只需将上述程序中的while(i<10)改为while(i<50) 即可。设 计循环结构时,通常将循环控制变量变化语句放在循环体的最后一行,虽然这不是 语法上的强制要求,但有利于程序的阅读和理解。例5-2中的循环变量i所在语句只起到 计数作用,并没参与计算,放在循环体的开始或结束位置效果是相同的。例5-1中的循环 变量i所在语句也可以放在循环体的开始处,程序修改为: #include<stdio.h> int main() { int i, sum=0; i=0; /*i 要从0 开始*/ while (i<=99) /*也可写成while(i<100)*/ { i++; sum=sum+i; /*循环控制变量i 参与求和计算*/ }p rintf("%d\n",sum); return 0; } 程序中需要修改循环控制变量i的值(初始化值为0),循环的判定条件也要做相应修 改(i<=99),显然程序的可读性不如例5-1所示的程序。 图5-2 do…while语句流程图 5.3 do…while语句 do…while语句实现直到型循环,先执行一次循环体语句,再判断循环条件。即使循 环条件不满足,循环体也至少被执行一次。 语句格式: do { 语句块; }while (循环条件表达式); do、while为关键字,语句块为循环体。while(循环 条件表达式)的后面必须有分号。执行过程如图5-2所 示。首先顺序执行循环体中的语句,然后检查循环条件 表达式,如果表达式值为真,再次顺序执行循环体;否则, 第5 章 迭代计算与循环结构1 07 循环结束。 利用do…while语句改写例5-1: #include<stdio.h> int main() { int i, sum=0; i=1; do { sum=sum+i; /*循环控制变量i 参与求和计算*/ i++; } while (i<=100); /*循环控制条件分号;不能缺省*/ printf("%d\n",sum); return 0; } do…while语句同样有两种简单表示形式。 (1)循环体为空语句。例如: i=0; do { }while (++i<10); /*当++i 为10 时,循环条件++i<10 不再成立,i=10 且循环结束*/ (2)循环体为表达式语句。例如: i=0; do { i++; }while (i<10); /*当++i 为10 时,循环条件++i<10 不再成立,i=10 且循环结束*/ 即使循环开始时没有满足循环条件,循环体仍然会执行一次。例如: int i=100; do{ printf("输出结果: i=%d\n",i); /*输出结果: i=100*/ }while(i<=10) ; 例5-3 编写程序,输入一个整数,输出这个数的位数。 #include<stdio.h> int main() { int n,count=0; scanf("%d",&n); while (n!=0) { count++; n=n/10; }p rintf("这是一个%d 位整数。\n",count); return 0; } 判断一个数的位数时,无论从左向右还是从右向左统计,都需要记住已经检查过的每 一位数,可以利用位数计数器计数(加1操作),直到统计完所有的位数,位数计数器的值 就是统计结果。基本解决思路如下: 1 08 程序设计基础(C 语言)(第4 版) (1)输入一个整数。 (2)计数加1,去除一位,处理剩余位数。 (3)重复执行步骤(2),直到没有剩余位数。 (4)输出计数结果。 实际上,因为不知道左侧第一个数字是第几位(否则就不必编写程序了),所以从左侧 开始去除一个位数很难。然而利用C语言整数除法的规则,从右侧去除一个数字的方法 却很简单,只需要除10取整即可。假设输入1234,第一次循环,count值为1,n的值变为 123;第二次循环,count值为2,n的值变为12;第三次循环,count值为3,n的值变为1; 第四次循环,count值为4,n的值变为0;此时不满足循环条件,循环结束(计数结果4)。 但是上述代码隐含着一个小错误,即当输入0 时程序输出的计数结果为0。因为当 n==0时,由于不符合循环条件而没有执行循环体。虽然可以在循环之前加上一个if语 句来处理n等于0的情况,例如: if (n==0) count=1; 但是,使用do…while语句是一个更好的选择。例如: #include<stdio.h> int main() { int n,count=0; scanf("%d",&n); do { count++; n=n/10; } while (n!=0); printf("这是一个%d 位整数。\n",count); return 0; } 当必须执行一次循环体时,采用do…while语句更好;而当有可能不需要执行循环体 时,选择while语句更好。 5.4 for语句 for语句是使用最广泛的一种循环控制语句,特别适合已知循环次数的情况。 1.for语句 语句格式: for (表达式1; 表达式2; 表达式3) { 语句块; } 表达式1通常为赋值表达式,用于对循环控制变量进行初始化,又叫初值表达式;表 第5 章 迭代计算与循环结构1 09 图5-3 for语句流程图 达式2为循环条件表达式;表达式3实现对循环控制变量 的修改,多数情况采用+ +/- - 表达式;语句块是循环体。 执行过程如图5-3所示。首先,计算表达式1的值,作为循 环控制变量初值。其次,判断表达式2是否成立,如果成 立,顺序执行循环体,否则退出循环。每一次循环体语句 执行结束时,都要重新计算表达式3的值,然后重新判断 表达式2是否成立,根据判断结果决定是否继续执行循 环体。利 用for语句改写例5-1: #include<stdio.h> int main() { int i, sum=0; for (i=1;i<=100;i++) { sum=sum+i; }p rintf("%d\n",sum); return 0; } i为循环变量,赋值表达式i=1为循环控制变量i赋初值;表达式i< =100为循环控 制条件;表达式i++保证每一次循环之后改变循环变量i的值,使循环能够正常结束。 for循环中常见的错误是循环控制条件使用不正确的表达式。例如,将上面代码中的 i<=100错误地写成i<100,则循环只进行99次。一个有效的解决方法是将问题的最终结 果值包含在循环条件中。 例5-4 编写程序,计算n 的阶乘n!(n!= 1×2×…×n)。 #include<stdio.h> int main() { int i, n; long fact=1; scanf("%d", &n); for (i=1; i<=n; i++) fact=fact*i; printf("%d! =%ld\n", n, fact); return 0; } 计算n!的过程是一个累乘求积的过程。调用scanf函数输入n 的值,设int型循环 变量i,i 从1至n 循环乘入fact中。为防止累乘结果溢出,采用longint型声明累乘器 变量fact。 2.for语句的常用省略格式 使用for语句时,经常会省略for语句中的某些表达式,for语句常用的几种省略格式 如下: (1)for( )。for语句的3个表达式都省略(注意,分号绝对不能省略),这是一个无 1 10 程序设计基础(C 语言)(第4 版) 限循环语句,与while(1)的功能相同,需要通过其他方式(如5.7.1节的break语句)结束 循环。 (2)for( 表达式2 表达式3)。省略表达式1,可以将表达式1写在for语句结构的 外面。例如: i=1; for (;i<=n; i++) fact=fact*i; 等价于 for(i=1; i<=n; i++) fact=fact*i; 如果循环控制变量的初值不是确定的值,而是需要通过for语句前面语句的执行计 算得到,可使用该形式。 (3)for(表达式1 表达式2 )。省略表达式3,C语言允许在循环体内改变循环控制 变量的值。该格式一般在循环控制变量呈非规则性变化,并且在循环体中有更新循环控 制变量的语句时使用。例如: for (n=1;n<=100;) { …n =3*n-1; /*循环控制变量的变化为1,2,5,14,…*/ … } (4)for(逗号表达式1;表达式2;逗号表达式3)。表达式1和表达式3可以是逗号表 达式。例如: for (n=1,m=100;n<m;n++,m--) { … } 表达式1同时为n和m 赋初值,表达式3同时改变n和m 的值。循环可以有多个 控制变量,逗号表达式可以与循环有关,也可以与循环无关。 (5)for(表达式1 表达式3)。省略表达式2,即循环条件一直成立,等价于while(1) 格式。 3.注意事项 使用for语句还需要注意以下几点。 (1)循环初始值(表达式1)、循环的条件(表达式2)和循环控制变量改变语句(表达 式3)中可以包含算术表达式。例如: n=10; for (i=n+1;i<=n*10;i++) 等价于 for(i=11;i<=100;i++) (2)尽管可以在for语句的循环体中修改循环控制变量的值,但在一般情况下,循环控制 变量仅用来控制循环过程,在循环体内尽量不改变其值,以免导致令人费解的结果和错误。 (3)表达式3可以自增/自减,或是加/减一个整数等多种形式。例如: for (i=100;i>=1;i--) /*循环控制变量从100 递减到1*/ for (i=0;i<=10;i+=2) /*循环控制变量从0 变化到10,每次增加2*/ for (i=10;i>=0;i-=2) /*循环控制变量从10 变化到0,每次减少2*/ 第5 章 迭代计算与循环结构1 11 当进行递增操作时,循环向上计数,表达式1的值要小于表达式2的值;当进行递减 操作时,循环向下计数,表达式1的值要大于表达式2的值;否则将造成死循环。 (4)for语句和while语句都是前判断结构,二者可以互相转换。 由于for语句将控制循环的3个要素写在一行中,看起来更为直观,因此更受欢迎。 例如: for (表达式1;表达式2;表达式3) { 语句块; } 等价于 表达式1; while (表达式2) { 语句块; 表达式3; } 例5-5 编写程序,统计某班30名学生C语言课程的平均成绩。 #include<stdio.h> int main() { double aver,sum; int i,score,n=30; sum=0; for (i=1; i<=n; i++) { scanf("%d",&score); /*读入第i 个学生成绩*/ sum=sum+score; /*成绩累加处理*/ }a ver=sum/n; /*计算平均成绩*/ printf("班级的平均成绩是%5.1 分。\n",aver); return 0; } 例5-5程序对已经确定人数的学生进行成绩处理,已知道循环次数,适合使用for循 环。循环控制变量的变化范围为1~30,每执行一次循环体,读入一个数据并累加一次, 直到30个数据全部输入后循环结束,计算全班的平均成绩。程序输出计算结果时采用的 格式%5.1表示占5位空间的双精度数且只保留一位小数。 5.5 循环语句对比 循环的本质就是当循环条件成立时反复执行循环体。C语言可以实现计数式循环和 标记式循环两种循环处理。 计数式循环用于处理知道准确循环执行次数的循环过程,又称为定数循环。在计数 式循环过程中,循环控制变量用来计算循环的次数。在每次执行循环体语句后循环控制 变量的值都要发生变化(递增或递减),当循环控制变量的值经改变达到了预定的循环次 数后结束循环。例5-4和例5-5都是计数式循环。 标记式循环适用于处理循环次数未知的循环过程,又称为不定数循环。在标记式循 环过程中,由于事先不知道准确循环的次数,因此需要在循环体中包含获取数据的语句, 以期望在某次数据处理后因不满足循环继续执行的条件而终止循环。这个确保退出循环 1 12 程序设计基础(C 语言)(第4 版) 的数据被称为标记值。标记值是利用一个专门的数据表示正常处理数据的结束,应该在 处理所有合法的数据后提供给程序,因此标记值必须不同于正常数据值。例5-3是标记 式循环。 while、do…while、for这3种循环语句形式各不相同,相互之间有一定区别,但主要 的结构成分都是循环体和循环控制条件。 (1)while、do…while循环一般用于标记式循环(循环次数未知),for循环通常用于 计数式循环(循环次数已知)。 (2)while、do…while循环通常将循环结束的条件放在while后面的表达式中,在循 环体中除反复执行的操作语句外,还应包含能够保证循环结束的语句(例如i++等循环变 量控制语句),for循环则用表达式2和表达式3表示循环结束的条件以及循环控制变量 的变化。 (3)while、for循环是前判断结构。如果循环条件一开始就不满足,则不执行循环体而 直接结束循环。因此循环体可能被执行0次(循环条件不满足)或多次(循环条件满足)。 do…while循环是后判断结构,循环条件无论是否成立,循环体至少要被执行1次。 (4)采用while、do…while循环时,循环变量的初始值操作一般在while和do… while语句之前完成,for语句中循环控制变量的初始化由表达式1实现。 3种循环语句都是根据循环条件决定是否重复执行,所以在循环体内部或循环条件 中必须存在改变循环条件的语句,否则会出现死循环等异常情况。实际应用中,同一个问 题既可以用while语句解决,也可以用do…while语句或for语句解决,3种循环语句格式 之间可以相互转化。选用的一般原则如下。 . 如果循环次数在执行循环体之前就已确定,一般用for语句;如果循环次数是由循 环体的执行情况确定,则采用while语句或do…while语句。 . 当循环体至少要执行1次时,采用do…while语句;反之则选用while或for语句。 5.6 循环嵌套 一个循环语句的循环体内包含另一个完整的循环语句,称为循环嵌套结构。3种循 环语句while、do…while和for语句可以互相嵌套,但被嵌套的循环结构一定是一个完整 的循环结构,即两个嵌套的循环结构之间不能相互交叉。循环嵌套结构中,每一层循环的 循环体都应该使用花括号“{}”括起来,即使循环体只有一条语句也应使用“{}”,以防止二 义性。