第5章

程序的循环结
构


思考题

1. 计算1+2+3+…+的值
。
对于这个问题,读者可能首先想到的是如下的数学方法
:
1+2+3+…+100=(1+100)
+ 
(2+99)+…
+ 
(50+51)=101×50=5050 
但与之类似地,对于以下倒数的求和,却不易直接用该数学方法计算: 

11 11+ 
2+ 
3+…+100 
其实,无论是计算1+2+3+…+100,还是计算1+1/2+1/3+…+1/100,二者都有一
个共性:重复进行了若干次有规律的加法运算的操作。
又比如,计算
n 
的阶乘n!, 即计算1×2×3×…(n-1)×n,它只不过是把上述重复进
行的若干次加法运算改成重复进行若干次乘法运算,求解过程的差异仅此而已。

2. 输入一个正整数n,输出它的所有因子。
求一个正整数
n 
的所有因子,也就是在[n]。若能够
1,区间上逐一用每一个整数去除
n 
整除,则该数就是
n 
的因子之一。它是一个重复进行整除测试的过程。

3. 利用下面的极限公式计算圆周率π的近似值: 
π 
111 

4 =1-3+ 
5 -7+
… 

计算机的运算速度快,最善于进行重复性的工作。在利用计算机解题时,往往可以把复
杂的不容易理解的求解过程转换为易于理解的操作的多次重复,这就是循环。就像上面的
计算n!和计算1+1/2+1/3+…+1/100,虽然我们不能像1+2+3…+100 那样找出一个
简单的数学公式就能得出结果,但我们可以通过构造循环结构的程序,交给计算机快速实现
重复操作而得出结果。上面的“思考题2”和“思考题3”,同样也是可以通过循环结构的计算
机程序来求解问题。


5.程序的循环控制
1 

在程序设计中,如果需要重复执行某些操作,就要用到循环结构。循环结构有两种形
式:当型循环和直到型循环,其对应的程序执行流程如图5-1所示。它们都是按照给定的
条件重复地执行某一个语句或语句组(复合语句), 区别在于:当型循环是先判断循环条件
是否满足,才确定是否开始循环操作;直到型循环则是先执行第一次循环操作,再按照循环
条件确定是否执行后续的循环操作。


图5-1 两种循环结构的执行流程图

循环结构是程序的3种基本控制结构中最复杂的一种
。
对于循环结构的设计,首先要明确两个问题
:


(1)哪些操作需要重复执行? 
(2)这些操作在什么情况下重复执行? 
这两个问题的解决分别对应循环体的设计和循环过程的设计。循环过程由3个要素组
成:循环过程控制量的初值、循环条件、使循环趋于结束的循环过程控制量的“增值”。只有明
确了这些问题,才能完整地设计出一个程序的循环结构,与此对应的程序执行流程如图5-2 

所示。
图5-2中标示的“循环变量”以及后续的同样表述,指的就是“循环过程控制量”。“循环

初始化”则包括循环变量的初始化和循环体相关量的初始化。
例如,计算1+2+3+…+99+100 的值,可以这样设计求解过程: 
首先设置一个累加结果变量sum,其初值为0,利用sum=sum+i 这个赋值操作,让计

算机重复进行加法运算和赋值操作。第一次加法运算时i的初值为1,之后的每次加法运算
的i值都比前一个i值多1,即:每次加法运算和赋值操作后使i=i+1,从而使i值依次取
1,2,99,

3,…,100 。通过重复100 次这样的加法运算和赋值操作,即可得到最后的结果。

显然,在这个求解过程中,循环(重复操作)的主体就是sum=sum+i 和i=i+1 。那么
如何控制循环的进程,确保正确地进行了100 次的重复操作呢? 也就是说,如何设计循环变
量的初值、循环条件和循环变量的“增值”? 

可以增加一个循环变量j,每一次循环后使j值增1, 增

j的初值为1; 即循环变量j的“ 

第
5 
章程序的循环结构

65

图5-2 完整的循环结构流程图

值”方式是j=j+1;当j值增加到100 以后结束循环,即循环条件是j<=100 。
根据以上分析,不难画出图5-3(a)所示的程序流程图,它是一个当型循环结构。


图5-3 计算1+2+3+…+99+100 的循环流程图

对图5-3(a)所示的流程图进一步分析,可以发现:其中所设置的循环变量j与循环体中
的i,在循环过程中其实是完全同步一致的,因而可以将i和j合二为一,简化程序代码。简
化后的程序流程如图5-3(b)所示。

依照上述“计算1+2+3+…+99+100”的算法逻辑,我们可以轻松地画出计算1+1/2+ 
1/3+…+1/100 和计算n! 的程序流程图,分别如图5-4和图5-5所示。图5-5中的f是用
于存放n! 计算结果的变量。

C语言提供了while语句、do-while语句和for语句这3种循环语句来构造程序的循环
结构。

66
C 
语言程序设计


第5 章 程序的循环结构 67 
图5-4 计算1+1/2+1/3+…+1/99+1/100的流程图图5-5 计算n! 的流程图
5.2 while语句
while语句的基本形式为: 
while(表达式) 
循环体语句; 
或 
while(表达式) 
{ 
多条循环体语句; 
}
while语句的常用形式: 
循环初始化; 
while(循环条件) 
{ 
循环体; 
循环变量改变; 
}
while语句的流程如图5-6所示。
说明: 
(1)while语句构造的循环是一个当型循环,一般用于根据某个条件控制循环的情形。
(2)在while语句的基本形式中,while后面的表达式是作为循环条件用的。当表达式
的值为真(非0)时,就反复执行循环体语句;一旦表达式的值为假(0值),即结束循环,它的
执行流程如图5-6(a)所示。为了使循环能够结束,循环体语句中一般应含有使循环趋于结
束的循环变量“增值”的语句。

68 C 语言程序设计
图5-6 while语句的流程图
(3)如前所述,一个完整的循环结构包括两大部分:循环主体和循环过程控制。同样
地,使用while语句构造一个完整的循环结构,也需要将这两个方面都构造完整。这正是
图5-6(b)所示的while语句常用形式所体现出来的循环结构的算法逻辑和程序流程。
(4)为了明确标识循环体的范围,注意正确使用锯齿形书写格式,使循环体语句向右缩
进对齐。
(5)当循环体中的语句不止一条时,必须用{}将循环体语句组合成一条复合语句。即
使循环体中的语句只有一条,也可以用{}将其括起来,以明确标识其循环体的性质,提高程
序的可读性。
(6)还需要注意的是:正常情况下,while(表达式)后面紧跟的是一个非空操作的循环
体语句,而不能紧跟一个分号“;”。如果while(表达式)后面紧跟的是一个分号,则表示循环
体为空,同时该分号还表示while语句已结束,其后的语句已不是循环体语句,从而造成了
空循环操作,并极有可能造成死循环。一旦程序运行出现死循环,一般可按Ctrl+Break组
合键以终止程序的运行。
例5.1 使用while语句建立循环结构的程序,计算1+2+3+…+99+100的值。
在本章开头,已经就本问题的求解过程做了详细分析和描述,它可以用迭代与递推的循
环结构来求解,迭代关系式是sum=sum+i,i值依次取1,2,3,…,99,100。按照while语
句的语法格式,对比图5-6(b)和图5-3(b)所示的流程图,不难写出求解本问题的程序代码。 
01 #include<stdio.h> 
02 main() 
03 { int i=1,sum=0; 
04 while(i<=100) 
05 { sum=sum+i; 
06 i++; 
07 } 
08 printf("sum=%d\n",sum); 
09 } 
程序运行结果:

第5 章 程序的循环结构 69 
sum=5050 
说明: 
(1)当循环体中的语句不止一条时,必须用{}将循环体语句组合成一条复合语句。如
果将本例中while语句循环体的{}去掉: 
while(i<=100) 
{ 
sum=sum+i; 
i++; 
} 
.变
成 
while(i<=100) 
sum=sum+i; 
i++; 
则while语句将变成死循环。因为此时的循环体语句只有“sum=sum+i;”,而“i++;”已
不属于循环体的语句,从而导致循环变量i不会出现任何变化,i总是保持初值1不变,循环
条件永远满足,这就造成了死循环(此时可按Ctrl+Break组合键以中断程序)。
(2)初学者还会常犯另一个错误:在while(表达式)后面紧跟一个分号。例如: 
while(i<=100) 
{ 
sum=sum+i; 
i++; 
} 
.变
成 
while(i<=100); 
{ 
sum=sum+i; 
i++; 
} 
此时,while(表达式)后面紧跟的分号“;”代表的就是循环体,只不过“它什么也没做”。
之后的{}部分已不属于循环体。这样也导致了循环变量i不会出现任何变化,循环条件永
远满足而造成死循环。
(3)如果将本题循环迭代的i值取值方向反过来,即i值依次取100、99、…、3、2、1,那
么,作为循环变量的i,其“增值”则是一个逐渐减值方向的“减值”,即i=i-1,同时循环条件
要调整为while(i>0)。循环部分的代码如下: 
01 int i=100,sum=0; 
02 while(i>0) 
03 { sum=sum+i; 
04 i--; 
05 } 
例5.2 使用while语句建立循环结构的程序,计算以下公式的值。
1+12
+13
+ … + 1 
99+ 1 
100 
本问题的求解方法与例5.1相同,它可以用迭代与递推的循环结构来求解,迭代关系式
是sum=sum+1.0/i,i值依次取1、2、3、…、99、100,程序流程如图5-4所示。
但需要注意的是: 
(1)存储累加和的变量sum,必须定义成浮点数类型(double型或float型)。
(2)每次循环迭代进来的是一个分数项1.0/i,要特别注意分数项的计算特点:分子和
分母要以浮点数方式进行运算。因为分子和分母不能是两个整数相除,否则依据C语言整
数除法的语法规则,分子和分母两个整数相除的结果只能保留整数部分。
要使分子和分母以浮点数方式进行运算,有两种解决办法:一是优先保障分子和分母

70 C 语言程序设计
整型数据的性质不变,但在进行分数运算时,将分子和分母的任一方临时转换为浮点型数据
来参与分数的运算;二是将分子和分母的任一方直接定义为浮点型变量,但这改变了分子和
分母整型数据的性质。本例按第一种办法进行处理。 
01 #include<stdio.h> 
02 main() 
03 { double sum=0; 
04 int i=1; 
05 while(i<=100) 
06 { sum=sum+1.0/i; //或sum=sum+1/(double)i; 
07 i++; 
08 } 
09 printf("sum=%f\n",sum); 
10 } 
程序运行结果: 
sum=5.187378 
例5.3 输入一个正整数n,输出它的所有因子。
分析:求一个正整数n的所有因子可以采用穷举法,对1~n的全部整数进行测试判
断,凡是能够整除n的均为n的因子。
程序: 
01 #include<stdio.h> 
02 main() 
03 { int n,i=1; 
04 printf("请输入一个正整数: "); 
05 scanf("%d",&n); 
06 printf("它的因子是: "); 
07 while(i<=n) 
08 { if(n%i==0) printf("%d, ",i); 
09 i++; 
10 } 
11 } 
例如,输入18得到的程序运行结果为: 
请输入一个正整数: 18 
它的因子是: 1, 2, 3, 6, 9, 18, 
5.3 do-while语句
do-while语句的基本形式: 
do 
循环体语句; 
while(表达式);

第5 章 程序的循环结构 71 
或 
do 
{ 
多条循环体语句; 
}while(表达式); 
do-while语句的常用形式: 
循环初始化; 
do 
{ 
循环体; 
修改循环变量; 
}while(循环条件); 
do-while循环语句的流程如图5-7所示。
图5-7 do-while语句的流程图
说明: 
(1)do-while语句构造的循环是一个直到型循环,一般用于根据某个条件控制循环。
(2)do-while语句与while语句的区别:do-while语句先执行一次循环体语句,然后再
对循环条件进行判断,循环体语句至少被执行一次;while语句则先判断后循环,有可能一
次也不执行循环体语句。当while后面表达式的值第一次循环就为真(非0)时,while语句
与do-while语句完全等价。
(3)还需要注意的是:在do-while语句中,while(表达式)后面有分号“;”,它是do-while 
语句结束的标志。如果漏掉了该分号,将造成语法错误,程序不能编译通过。而在while语
句中,正常情况下while(表达式)后面不能紧跟一个分号“;”,否则将造成空循环,并极有可
能导致死循环。前面已对此做了详细说明,在此不再赘述。
例5.4 将例5.1中计算1+2+3+…+99+100的程序,改用do-while语句实现。
对比图5-6(b)所示的while语句流程图和图5-7(b)所示的do-while语句流程图,再参

72 C 语言程序设计
照例5.1的处理方法,按照do-while语句的语法格式,不难写出如下的程序代码。 
01 #include<stdio.h> 
02 main() 
03 { int i=1,sum=0; 
04 do 
05 { sum=sum+i; 
06 i++; 
07 }while(i<=100); 
08 printf("sum=%d\n",sum); 
09 } 
5.4 for语句
for语句的基本形式: 
for(表达式1; 表达式2; 表达式3) 
循环体语句; 
或 
for(表达式1; 表达式2; 表达式3) 
{ 
多条循环体语句; 
} 
图5-8 for语句的流程图
for语句的流程图如图5-8所示。
说明: 
(1)与while语句一样,for语句构造的循环也是一个当型
循环。如
图5-8所示,for语句的执行流程是:首先计算表达式1的
值(只计算一次),再计算表达式2(循环条件)的值,并根据表达式
2的值判断是否执行内嵌的循环体语句;如果表达式2的值为真
(非0)时,则执行循环体语句,并紧接着计算表达式3的值,再回
头计算表达式2的值,并根据表达式2的值判断是否执行循环
体,……,如此循环往复。一旦表达式2的值为假(0值),即结束
循环。
(2)对比图5-8“for语句的流程图”和图5-2(a)“完整的循环结构流程图-当型循环结
构”,显而易见,两者可以完全对应。for语句括号中的3个表达式“表达式1、表达式2、表达
式3”,分别对应“循环初始化、循环条件、循环变量改变”。即与循环结构基本逻辑完全对应
的for语句的应用形式: 
for(循环初始化;循环条件;循环变量改变) 
{ 
循环体语句; 
}

第5 章 程序的循环结构 73 
(3)注意在“for(表达式1;表达式2;表达式3)”中,分隔3个表达式的是两个分号“;” 
而不是逗号。
(4)与while语句和do-while语句一样,当循环体中的语句不止一条时,必须用{}将循
环体语句组合成一条复合语句。同样还要注意正确使用锯齿形书写格式,使循环体语句向
右缩进对齐,以明确标识循环体的范围,提高程序的可读性。
(5)还需要注意的是:正常情况下,“for(表达式1;表达式2;表达式3)”后面紧跟的
是一个非空操作的循环体语句,而不能紧跟一个分号“;”,否则将造成空循环。这样的空循
环,虽然不会像while语句那样导致死循环,但循环的结果却是“什么都没做”。
(6)表达式2是空语句时,认为是非0,即真。如“for(i=0;;i++);”就是死循环。
例5.5 将例5.1中计算1+2+3+…+99+100的程序,改用for语句实现。
分析:for语句与while语句一样属于当型循环,它们求解问题的算法逻辑是一致的。
因而可以依照图5-3(b)所示的程序流程图和for语句的语法格式,写出下面的程序代码。 
01 #include<stdio.h> 
02 main() 
03 { int i,sum; 
04 for(sum=0,i=1;i<=100;i++) 
05 { sum=sum+i; 
06 } 
07 printf("sum=%d\n",sum); 
08 } 
例5.6 按照格雷戈里公式:π
4=1-13
+15
-17
+…,求π的近似值,直到最后一项的
绝对值小于10-6。
分析:图5-4中已经给出了计算1+1/2+1/3+…+1/100的程序流程图,而本题所使
用的格雷戈里公式与其非常相似,同样可以把它看成一个不断累加求和的过程,所不同的
是:每一次累加进来的分数项,正负符号位要改变,分母取值的变化率不同,累加的次数是
未知的,但累加结束的时机是确定的,即循环条件仍然是明确的。
若累加和用sum 表示,每次被加的分数项用t表示,分数项的分母用n表示,分数项的
符号位(正负号)用sign表示。参照前面类似问题求解过程的分析和循环流程的设计,可以
发现求解本问题的循环结构,其循环体包括sum=sum+sign*t,以及t的取值(t=1/n)和
符号位sign的转换。sign的初值取1,则每一次循环,通过sign=-sign的运算,即可使符
号位正负转换。显然,这也是通过迭代算法构造的循环,迭代关系式有两个:sum=sum+ 
sign*t和sign=-sign。
那么循环的进程又该如何设计呢? 可以将分数项的分母n直接作为循环变量,n的初
值取1,每一次循环后使n=n+2,从而使n值依次取1、3、5、7……直到1/n小于0.25× 
10-6,即循环条件是t≥10-6。
通过以上分析,不难写出下面的程序代码。 
01 #include<stdio.h> 
02 main() 
03 { int sign=1,n=1; //sign 用于设置正负号,n 代表分母