第 6章 循环控制
6.1 概述在上一章中,我们已经学习了算法的选择结构的 C语言程序实现及其程序流程控制转移方式。本章将讨论最后一种算法的基本结构 ── 循环结构的 C语言程序实现及其程序流程控制转移方式 ── 循环控制。
所谓 循环控制,即满足一个指定的条件,每次使用不同的数据,对程序中的计算或处理步骤完全相同的程序段,重复计算若干次的程序控制。
int s=1,t=1;
s=s*t; t++;
s=s*t; t++;
s=s*t; t++;
s=s*t; t++;
s=s*t;
printf(,%d”,s) ;
如计算 5!,若用顺序结构实现:
在该程序段中,语句 s=s*t; t++; 连续执行了四次,
而且每次使用的 s 和 t的值都不同,这就构成了循环的条件 。
因此,程序应该使用循环控制方式来实现 。
int s=1,t=1;
while ( t<=5 )
{
s=s*t ;
t++ ;
}
printf(,%d”,s) ;
上面的程序如果用 C语言的循环结构来实现,可以写成如下简单形式:
很明显,这样的程序不仅简单,而且逻辑性更强 。
6.2 goto 语句以及用 goto 语句构成循环选择控制和循环控制都依赖于条件测试,条件测试的结果决定了程序执行的方向或路径,所以它们都是 有条件 的程序控制转移。
相应的,在计算机程序设计语言中还提供另一类程序流程控制语句 ── 转向语句。 用这类语句来实现程序的控制转移不依赖于任何条件,故称之为 无条件 控制转移。
本节讨论实现程序的无条件控制转移的 goto语句的使用。
1,goto 语句格式
goto 语句标号
2,执行与功能无条件地转向 语句标号 后的语句处执行。
3,语句标号形式标号,
a) 按标识符规则直接定义,不需进行说明;
b),号不属于标号,作为标号与语句间的分隔符;
d) 标号的作用域是所在函数,同一函数中的所有标号不能相同,但可与函数中的变量名同名,不会产生冲突;
c) 标号用来标识函数中的某个位臵,作为 goto语句的转向目标;
e) 不可以从一个函数中用 goto 语句转向另一函数中去执行; 但可转到本函数中的任何地方 ( 包括循环语句,if
语句,switch语句,复合语句中 ),并从那一点继续执行下去,且按原语句的功能控制程序的执行 。
4,用 goto 语句和 if 语句实现循环
6.3 while 语句
while 循环语句是一种“当型”循环语句。所谓
“当型” 循环,亦即当某个给定的条件成立时执行循环体,
否则停止循环的执行。
1,while 语句格式
while( e)
语句其中的,e” 可为任何表达式,必须括在小括号中,它的值作为 while循环继续与否的判断条件。 在小括号后不能加分号。 最简单的表达式可以是一个单一的常数。
下面列出的表达式都可以作为 while循环的判断条件。
( ( ch=getchar ( ) ) !=?A? )
( x!=0 )
( x==0 )
( x )
( !x )
( 1 )
,语句,部分即为 while 循环要重复执行的循环体。
在这个位臵上仅能是一条C语言语句,也可以是一条空语句(仅有一个分号,表示循环体为空)。使用复合语句则能重复执行一组语句。
2,while语句执行过程及功能首先对括号中的表达式,e”求值,若其值为真(非
0),执行循环体语句,否则(表达式值为 0)结束并退出
while循环语句。下图描述了这个执行过程。
e 非 0

语句真假计算 e的值
3,说明与注意
a) 先对 e 求值并判断,循环体可能一次都不执行 。 每执行一遍循环体,都要计算表达式 e的值,并以此决定是否再次执行循环体。
b) 表达式 e 本身及循环体中应当包含对表达式 e中一些控制循环变量的修改运算。因此,在 while 循环体中或表达式 e 本身,应该有使表达式 e 的值趋于假(趋于 0)的操作,使得经过有限次循环操作后能退出循环 ; 或存在 exit、
break,goto,等强制结束循环的手段。否则,该 while 循环就会无休止地循环下去。
c) while循环适用于循环次数不确定的情况。
int gcd( int a,int b )
{
int c ;
while( b!=0 )
{
c = a%b ;
a = b ;
b = c ;
}
return a ;
}
4,应用例程序 1,求两个整数的最大公约数 ( 辗转相除法 ) 。
#define YES 1 /* 输入的单词未完 */
#define NO 0 /* 下面是一个新单词 */
#include <stdio.h>
main ( )
{
int c,n,nw,nc,inword = NO ;
nl = nw = nc = 0 ;
while ( (c = getchar ( ) ) != EOF ) {
++nc ;
if ( c = =?\n? )
++nl ;
if ( c = = || c = =? \n? || c = =?\t? )
inword = NO ;
else if ( inword = = NO ) {
inword = YES ;
++nw ;
}
}
printf (,nl=%d nw=%d nc=%d\n”,nl,nw,nc ) ;
}
程序 2,统计输入的行数,单词的个数 ( 假定一个单词是不含有空格的字符序列 ),以及输入的总的字符个数 。
#include <stdio.h>
#include <math.h>
main ( )
{
double s = 0,x = 1 ;
int sign = 1 ;
long k = 1 ;
while ( fabs (x) >1e-8 ) {
s += x ;
k += 2 ;
sign *= -1 ;
x = sign/(double)k ;
}
s *= 4 ;
printf (,pi=%lf\n”,s ) ;
}
程序输出,pi=3.141593
程序 3,计算 π/4≈1 - 1/3 + 1/5 - 1/7 + ……
6.4 do-while 语句
do_while是一种适用于构造“直到型” 循环的循环语句。它也适用于对循环次数不确定情况的循环处理。
do_while循环与 while循环语句的功能是类似的。 它们在语法上的区别在于,while语句的循环条件判断在前,
do_while语句的循环条件判断在后。
1、语句格式
do
语句 /* 循环体 */
while (e) ; /* 最后的分号必须有 */
2、语句功能
e非 0

语句真假计算 e的值
3,说明与注意
a) 先执行循环体,后计算 e 的值并判断,循环体至少执行一次 。
b) 每执行一遍循环体后,都要重新计算 e 的值以决定是否再次进入循环体 。
c) 在表达式 e本身或在循环体中,应当包含对表达式 e中某些操作数的修改操作,使得循环能终止 。
d) 循环体可以是单条语句,复合语句或空语句 。 单条语句时,为防止与 while语句混淆,通常也采用复合语句形式 。
e) do和最后的 while是一个整体,特别是属于这个整体的最后的那个分号不能丢掉,否则语法不完整 。
#include <math.h>
main ( )
{
double x,s1,s2 = 1 ;
a,scanf (,%lf,,&x ) ;
if ( x <= 0 ) {
printf (,x ≤0 \n,) ;
goto a ;
}
do {
s1 = s2 ;
s2 = 0.5*( s1+x/s1 ) ;
} while ( fabs ( s2-s1 ) >= 1e-6 ) ;
printf (,\n sqrt(%lf ) = %lf\n”,x,s2 ) ;
}
例 1,用牛顿迭代公式,sn+1 =? × ( sn+ x/sn )
求当 | sn+1-sn | ≤ 10-6时 x 的平方根 。
算法思想,
例 2:用牛顿迭代法求解一元非线性方程 f (x) = 0 在 x0 附近的一个实根 。
迭代公式:
xn+1 = xn – f (xn) /f ’( xn ) ( n = 0,1,2 ….,)
当 | f ( xn+1 ) | < eps 或者 | xn+1- xn |< eps 时,则认为 xn+1
是方程 f ( x ) = 0 在 x0 附近的一个实根。
例如:
求方程 6-5x+4x2-3x3=0 在 1.0附近的一个实根 。
f (x) = 6 - 5x + 4x2 - 3x3
= 6 – x ( 5 – x (4-3x) ) (霍纳氏方案 )
f?(x) = -5 + 8x - 9x2
= -5 + x ( 8-9x )
#include <stdio.h >
#include <math.h>
double f ( double x )
{
return 6-x*( 5-x*(4-3*x ) ) ;
}
double f1 ( double x )
{
return -5+x*( 8-9*x ) ;
}
main ( )
{
double x=1.0,x1,eps=1e-6 ;
do {
x1=x ;
x=x1-f(x1)/f1(x1) ;
} while ( fabs ( f(x) ) >= eps || fabs (x-x1) >= eps ) ;
printf (,x=%lf”,x ) ;
}
程序输出,x=1.265328
6.5 for 语句
for循环语句是C中三种循环语句中最常用的循环语句 。 它最适合于循环次数已知的循环程序设计,也可用它来实现“当型”及“直到型”循环。
1,语句格式
for ( e1 ; e2 ; e3 )
语句其中:
e1,e2,e3 都是表达式。
2、功能
e2非 0
语句真假计算 e1
计算 e3
计算 e2
3,for循环语句使用说明
(1) e1,e2,e3 可以是任何形式的表达式,它们之间用分号隔开 。 这三个表达式均可缺省,当它们同时缺省时其后的分号必须保留,此时 for 语句有如下简单的形式:
for ( ; ; )
语句
,表达式 e1,和,表达式 e3”无标准缺省值,仅表示在这个位臵为空 ;,表达式 e2”有标准缺省值,其缺省值恒为 1。
25
在通常的意义下,“表达式 e1”、“表达式 e2”、“表达式 e3,规定了循环的三要素:
“表达式 e1” 常常是一个赋值表达式,用来设臵循环控制变量及其初值; (注意:表达式 e1仅被求值一次! )
“表达式 e2” 用来控制循环继续与否,在这个位臵上一般是一个与循环控制变量相关的关系表达式或逻辑表达式 ;
“表达式 e3” 俗称步长,用于每次修改循环控制变量的值。
需要强调,我们这里解释的 e1,e2 和 e3 仅指它们的一般意义,是按一般的程序设计语言中的相应语句功能解释的,
事实上它们可以是任何与循环无关的表达式。
(3) 从 for循环的处理流程还可看出,e1,e3 可与 e2 无关。
因为无论 e1与 e3的值的变化情况如何,以及它们缺省与否,
for 循环处理时总是要转去判断 e2 的值。这就是说,若 e2
存在,而且 e1,e3 与 e2 无关,或者在 e1与 e3 都缺省的情况下,循环继续与否仅能依赖 e2值的变化情况,而此时 e2
的值仅能依赖自身执行的改变,以及循环体中对它的改变,
或在循环体中使用强制停止循环执行的手段。
(2),语句,位臵处仅能是一条合法的C语言语句,它就是
for循环语句要重复执行的部分,通常称之为循环体 。
(4) 在由小括号括住的循环控制参数中,允许同时设臵多个循环变量,与此相应也允许同时对多个循环控制变量的值进行修改。这是因为 e1和 e3都可以是任何表达式,
那么当然也可以是逗号表达式,而正是逗号表达式的特性带来了 for语句的这种灵活性。例如,
for ( x=0,y=100 ; x<100 ; x++,y-- )
(5) 在某些情况下,作为循环体的语句完全可以与 e3
一起写成一个逗号表达式,放到 for语句的 e3的位臵处,使得循环体为空,而程序功能不变。如对于,
main ( )
{
int i ;
for ( i=0 ; i<10 ; putchar ( getcha r( ) ),i++ ) ;
}
main ( )
{
int i ;
for ( i=0 ; i<10 ; i++ )
putchar ( getchar ( ) ) ;
}
完全可以写成,
4,循环次数已知的循环例
a) for ( j=0 ; j<100 ; j++ ) { ……… }
循环 100次,脱离循环时 j=100
b) for ( j=100 ; j >= 1 ; j -- ) { ……… }
循环 100次,脱离循环时 j=0
c) for(j=0 ; j<=20 ; j+=2 ) { ……… }
循环 11次,脱离循环时 j=22
5,循环次数未知的循环例
scanf (,%d”,&x ) ;
for ( sum=0,count=0 ; x>=0 ; count++ ) {
sum+=x ;
scanf (,%d”,&x ) ;
}
6,常见错误
(1) for ( i=0,s=0 ; i<10 ; i++ ) ;
s=s+i ;
(2) 循环变量用实型变量,导致循环次数不准确。
float i ;
for ( i=13.2 ; i==0 ; i=i-0.2 )
{ …… }
(3) 假定有,unsigned j ; 那么对于:
for ( j=15 ; j<0 ; j-=2 ) { ……… }
循环多少次? 0
而对于 for ( j=15 ; j>0 ; j-=2 ) { ……… }
循环体又执行多少次? endless
#include <stdio.h>
main ( )
{
int fahr ;
for ( fahr=0; fahr <=300; fahr+=20 )
printf (,%4d%8.1f\n”,fahr,5.0/9.0*(fahr-32) ) ;
}
7,程序例例 1 按公式 C = 5/9 × (F-32) 将华氏温度转换成摄氏温度,
并产生一张华氏 0~ 300度 ( 每隔 20 度输出一次 ) 与对应的摄氏温度之间的对照表 。
#include <stdio.h>
main ( )
{
int t,k,n ;
scanf (,%d”,&n ) ;
k=n ;
if ( n<0 ) {
putchar (?-?) ;
k=n=-n ;
}
for ( t=1 ; n>10 ; t*=10,n/=10 ) ;/*n=323 then
x=100*/
for ( ; t>1 ; t/=10 ) {
putchar( k/t+?0? );
k%=t ;
}
putchar ( k+?0? ) ;
}
例 2:把一个 int型数转换成对应的字符串并打印出来 。
例 3:用梯形法计算定积分。
将曲线 f(x),x轴、直线 a到 f(a),和 b到 f(b)围成的区域分成 n个等分的小梯形,每个小梯形 si的面积为:
si = h*(f(xi)+f(xi+1))/2
h = (b-a)/n
x0 = a,x1 = a+h,xi = a+ih,
xn = a+nh=b
))(.,,)(
2
)()(()(
11
0


n
n
b
a
xfxfxfxfhdxxf
double f ( double x )
{
return x*x+12*x+4 ;
}
main ( )
{
int a,b,n,i ;
double h,s,x ;
scanf (,%d%d%d”,&n,&a,&b ) ;
h=(b-a)/n ;
s=( f(a) + f(b) )/2 ;
x=a ;
for ( i=1 ; i<n ; i++ )
{
x += h ;
s += f(x) ;
}
printf (,s=%lf”,s*h ) ;
}

4
1
2 )412( dxxx
若有 f(x)=x2+12x+4 则程序如下:
6.6 循环的嵌套因为前面讨论的三种循环结构的循环体的,语句,
都可以是任何一条合法的C语言语句,所以作为循环体的 语句,当然可以是另一个循环语句。
在一个循环结构的循环体内又包含另一循环结构的循环称之为嵌套循环。
依此类推,内嵌的循环体内又可包含另一循环语句,
这样就形成了多重嵌套循环。至于嵌套重数的多少C语言不加限制 。
例 1,嵌套循环的例。
对于
-5 ≤ x ≤ 11
-10 ≤ y ≤ 9
-6 ≤ z ≤ 18
求方程 x3+y3+z3=3 的全部整数解。
#include <math.h> /*这是必须的 */
#include <stdio.h>
main ( )
{
int x,y,z ;
printf (,\n%5s%5s%5s\n\n”,“x”,“y”,“z” ) ;
for ( x=-5 ; x<12 ; x++ )
for ( y=-10 ; y<10 ; y++ )
for ( z=-6 ; z<19 ; z++ )
if(( int)pow ( x,3 ) + (int)pow ( y,3) +
(int)pow ( z,3 ) == 3 )
printf (,%5d%5d%5d\n”,x,y,z ) ;
}
程序的输出是:
x y z
-5 4 4
1 1 1
4 -5 4
4 4 -5
#include <math.h>
#include <stdio.h>
main ( )
{
register i,j ;
printf (,%6s%6s%6s%6s\n”,“x^1”,“x^2”,“x^3”,“x^4” ) ;
for ( i=1 ; i<28 ; i++ )
putchar (196) ;
putchar (?\n?) ;
for ( j=1 ; j<11 ; j++ )
for (i=1 ; i<5 ; i++ )
printf ( (i==4)?,%6d\n”,,%6d,,(int)pow (j,i) ) ;
}
例 2 产生一张 1 ~ 10 的一次方到四次方的表格 。
41
x^1 x^2 x^3 x^4
──────────
1 1 1 1
2 4 8 16
3 9 27 81
4 16 64 256
5 25 125 625
6 36 216 1296
7 49 343 2401
8 64 512 4096
9 81 729 6561
10 100 1000 10000
程序输出:
6.7 几种循环的比较
1) 同一个循环问题可以用三种不同的循环语句来实现,即它们可以互相转换。如,
while 语句等价于 for语句中,表达式 e1” 和,表达式
e3”
缺省的情况 。
43
反之,把 for 语句改成 while 语句,只要在 while 语句之前先安排要处理的 for 语句中的,表达式 e1”,而把,表达式
e3”和,语句” 部分 写成复合语句 作为 while的循环体语句
(“表达式 e3” 一定要为该复合语句中的最后一条语句),最后把 for循环语句中的,表达式 e2” 作为 while 后的条件
“表达式” 。改写后的形式应是:
2) 在实际应用中,do_while 循环的使用要比 for 和
while 循环的使用少得多。有人统计,do_while 在所有循环的使用中只占 5%。 然而有时它的使用是很有价值的。
3) do_while 循环和 while 循环适用于循环次数事先不知的情况; for循环适用于循环次数事先已知的情况 。
6.8 break 和 continue 语句
1,break 语句
1) 语句格式,break ;
2) 使用约束:
只能在 switch,while,do-while,for语句中使用 。
3) 语句功能:
强制退出所在的结构,转到相应结构后的第一条语句执行 。
4) 使用例:
main ( )
{
int t,g [100 ] ;
……
for ( t=0 ; t<100 ; t++ )
if ( g[t] <0 ) break ;
……
}
这个程序段用于检查具有 100个元素的数组中是否有负数 。 一旦发现一个负数,尽管 t<100 仍成立,但由于遇到了 break 语句,所以立即结束循环的执行 。
在程序设计中常常需要去检查一组数据中是否存在某个元素,当在检查中一旦发现指定的元素存在,自然就不必继续进行这种检查 。 下面的程序段体现了这种处理要求:
2,continue语句用 break 语句来强制结束循环的执行是很方便的。然而在进行循环程序设计时常常遇到另一种特别的情况:
当程序执行到达循环体中的某一点时,根据某一条件而不必继续执行该点后面的循环体部分而 提前结束本轮循环 的执行,但这不是终止循环语句的执行! 而是回到循环的条件测试部分( 对 while 和 do_while,即去判别
while 后的,表达式” ; 对 for 循环则回到,表达式 3”
的处理部分)决定是否重新开始新的一轮循环。
这种情况的处理用 break 语句是不能实现的,只能用
C语言提供的 continue语句才能解决。
48
continue 语句的语法:
1) 语句格式,continue ;
2) 使用约束:
只能在,while,do-while,for 语句中使用 。
3) 语句功能跳过所在循环体剩余语句,继续进行下一轮循环 。
continue 语句的使用 例:
在该例中,当 i%17!=0 成立时表明除不尽,那么这个数不被输出,即下面的 printf 语句不被执行,直接去处理
i++,然后再进行 i<=1000的测试,决定是否继续循环下去。
下面程序的功能用来输出 1000 以内的能被 17 整除的数 。
#include <stdio.h>
main ( )
{
int i ;
for ( i=17 ; i<=1000 ; i++ ) {
if ( i%17 != 0)
continue ;
printf (,%d\n”,i ) ;
}
}