循環(huán)控制結(jié)構(gòu)(又稱重復(fù)結(jié)構(gòu))是程序中的另一個(gè)基本結(jié)構(gòu)。在實(shí)際問題中,常常需要
進(jìn)行大量的重復(fù)處理,循環(huán)結(jié)構(gòu)可以使我們只寫很少的語句,而讓計(jì)算機(jī)反復(fù)執(zhí)行,從而完成大量類同的計(jì)算。
C語言提供了while語句、do…while語句和for語句實(shí)現(xiàn)循環(huán)結(jié)構(gòu)。
3.4.1while語句
while語句是當(dāng)型循環(huán)控制語句,一般形式為:
while<表達(dá)式>語句;
語句部分稱為循環(huán)體,當(dāng)需要執(zhí)行多條語句時(shí),應(yīng)使用復(fù)合語句。
while語句的流程圖見圖3-8,其特點(diǎn)是先判斷,后執(zhí)行,若條件不成立,有可能一次也不執(zhí)行。
[例3-11]求n!
分析:n!=n*(n-1)*(n-2)*..2*1,0!=1。即S0=1,Sn=Sn-1*n??梢詮腟0開始,依次求出S1、S2、…Sn。
統(tǒng)一令S等于階乘值,S的初值為0!=1;變量i為計(jì)數(shù)器,i從1變到n,每一步令S=S*i,
則最終S中的值就是n!。
流程圖見圖3-9,程序如下:
考察圖3-9中循環(huán)部分的流程圖可以看出,在循環(huán)前各變量應(yīng)有合適的值(s=1),另外,控制循環(huán)結(jié)束的變量(此處為i)必須在循環(huán)體中被改變,否則,循環(huán)將無限進(jìn)行下去,成為死循環(huán)。
本題中,將多項(xiàng)式的每一項(xiàng)用t表示,s代表符號(hào),在每一次循環(huán)中,只要改變s、n的值,就可求出每一項(xiàng)t。
一般情況下,while型循環(huán)最適合于這種情況:知道控制循環(huán)的條件為某個(gè)邏輯表達(dá)式的
值,而且該表達(dá)式的值會(huì)在循環(huán)中被改變,如同例3-12的情況一樣。
3.4.2do…while語句
在C語句中,直到型循環(huán)的語句是do…while,它的一般形式為:
do語句while<表達(dá)式>
其中語句通常為復(fù)合語句,稱為循環(huán)體。
do…while語句的流程圖見圖3-10,其基本特點(diǎn)是:先執(zhí)行后判斷,因此,循環(huán)體至少被執(zhí)行一次。
但需要注意的是,do…while與標(biāo)準(zhǔn)的直到型循環(huán)有一個(gè)極為重要的區(qū)別,直到型循環(huán)是當(dāng)條件為真時(shí)結(jié)束循環(huán),而do…while語句恰恰相反,當(dāng)條件為真時(shí)循環(huán),一旦條件為假,立即結(jié)束循環(huán),請(qǐng)注意do…while語句的這一特點(diǎn)。
例[3-13]計(jì)算sin(x)=x-x3/3!+x5/5!-x7/7!+…
直到最后一項(xiàng)的絕對(duì)值小于1e-7時(shí)為止。
分析:這道題使用遞推方法來做。
讓多項(xiàng)式的每一項(xiàng)與一個(gè)變量n對(duì)應(yīng),n的值依次為1,3,5,7,…,從多項(xiàng)式的前一項(xiàng)
算后一項(xiàng),只需將前一項(xiàng)乘一個(gè)因子:
(-x2)/((n-1)*n)
用s表示多項(xiàng)式的值,用t表示每一項(xiàng)的值,程序如下:
#include <math.h>
# include <stdio.h>
m a i n ( )
{
double s,t,x ;
int n ;
printf(“please input x :”);
scanf(“%lf”,&x);
t=x;
n=1;
s=x;
do
{
n=n+2;
t=t*(-x*x)/((float)(n)-1)/(float)(n);
s=s+t;
}while(fabs(t)>=1e-7);
printf(“sin(%f)=%lf,”x,s);
}
運(yùn)行結(jié)果如下:
RUN?
pleaseinputx:1.5753?
sin(1.575300)=0.999990
RUN?
pleaseinputx:-0.65?
sin(-0.650000)=-0.605186
3.4.3for語句
for語句是循環(huán)控制結(jié)構(gòu)中使用最廣泛的一種循環(huán)控制語句,特別適合已知循環(huán)次數(shù)的情
況。它的一般形式為:
for(<表達(dá)式1>;<表達(dá)式2>;<表達(dá)式3>)語句
for語句很好地體現(xiàn)了正確表達(dá)循環(huán)結(jié)構(gòu)應(yīng)注意的三個(gè)問題:
1)控制變量的初始化。
2)循環(huán)的條件。
3)循環(huán)控制變量的更新。
表達(dá)式1:一般為賦值表達(dá)式,給控制變量賦初值;
表達(dá)式2:關(guān)系表達(dá)式或邏輯表達(dá)式,循環(huán)控制條件;
表達(dá)式3:一般為賦值表達(dá)式,給控制變量增量或減量。
語句:循環(huán)體,當(dāng)有多條語句時(shí),必須使用復(fù)合語句。
for循環(huán)的流程圖如圖3-11,其執(zhí)行過程如下:
首先計(jì)算表達(dá)式1,然后計(jì)算表達(dá)式2,若表達(dá)式2為真,則執(zhí)行循環(huán)體;否則,退出for循環(huán),執(zhí)行for循環(huán)后的語句。如果執(zhí)行了循環(huán)體,則循環(huán)體每執(zhí)行一次,都計(jì)算表達(dá)式3,然后重新計(jì)算表達(dá)式2,依此循環(huán),直至表達(dá)式2的值為假,退出循環(huán)。
[例3-14]計(jì)算自然數(shù)1到n的平方和。
# include <stdio.h>
# include <math.h>
main ( )
{
int i;
float s;
printf(“please input n :”);
scanf(“%d”,&n);
s=0.0;
for(i=1;i<=n;i++)
s=s+(float)(i)*(float)(i);
printf(“1*1+2*2+…+%d*%d=%f,nn”,n,s);
}
運(yùn)行結(jié)果如下:
RUN
please input n:5
1*1+2*2+…+5*5=55.000000
for語句的幾種格式
for語句的三個(gè)表達(dá)式都是可以省略的,但分號(hào)“;”絕對(duì)不能省略。
a.for(;;)語句;
這是一個(gè)死循環(huán),一般用條件表達(dá)式加break語句在循環(huán)體內(nèi)適當(dāng)位置,一旦條件滿足時(shí),
用break語句跳出for循環(huán)。
例如,在編制菜單控制程序時(shí),可以如下:
for(;;)
{
printf(“please input choice(Q=Exit):”);顯/*示菜單語句塊:*/
scanf(“%c”,&ch);
if(ch==’Q’)or(ch==’q’)break;語/*句段*/
}
b.for(;表達(dá)式2;表達(dá)式3)
使用條件是:循環(huán)控制變量的初值不是已知常量,而是在前面通過計(jì)算得到,例如:
i=m-n;
??
for(;i<k;i++)語句;
c.for(表達(dá)式1;表達(dá)式2;)語句
一般當(dāng)循環(huán)控制變量非規(guī)則變化,而且循環(huán)體中有更新控制變量的語句時(shí)使用。
例如:
for(i=1;i<=100;)
{
??
i=i*2+1;
??
}
d.for(i=1,j=n;i<j;i++,j–)語句;
在for語句中,表達(dá)式1、表達(dá)式3都可以有一項(xiàng)或多項(xiàng),如本例中,表達(dá)式1同時(shí)為i和j賦
初值,表達(dá)式3同時(shí)改變i和j的值。當(dāng)有不止一項(xiàng)時(shí),各項(xiàng)之間用逗號(hào)“,”分隔。
另外,C語言還允許在循環(huán)體內(nèi)改變循環(huán)變量的值,這在某些程序的設(shè)計(jì)中是很有用的。
到此,我們已經(jīng)學(xué)習(xí)了C語言中三種循環(huán)控制語句while、do…while和for語句,下面再討論兩個(gè)問題:
三種語句的選用
同一個(gè)問題,往往既可以用while語句解決,也可以用do…while或者for語句來解決,但在
實(shí)際應(yīng)用中,應(yīng)根據(jù)具體情況來選用不同的循環(huán)語句,選用的一般原則是:
1)如果循環(huán)次數(shù)在執(zhí)行循環(huán)體之前就已確定,一般用for語句;如果循環(huán)次數(shù)是由循環(huán)體
的執(zhí)行情況確定的,一般用while語句或者do…while語句。
2)當(dāng)循環(huán)體至少執(zhí)行一次時(shí),用do…while語句,反之,如果循環(huán)體可能一次也不執(zhí)行,
選用while語句。
循環(huán)的嵌套
一個(gè)循環(huán)的循環(huán)體中有另一個(gè)循環(huán)叫循環(huán)嵌套。這種嵌套過程可以有很多重。一個(gè)循環(huán)
外面僅包圍一層循環(huán)叫二重循環(huán);一個(gè)循環(huán)外面包圍兩層循環(huán)叫三重循環(huán);一個(gè)循環(huán)外面包
圍多層循環(huán)叫多重循環(huán)。
三種循環(huán)語句for、while、do…while可以互相嵌套自由組合。但要注意的是,各循環(huán)必須
完整,相互之間絕不允許交叉。如下面這種形式是不允許的:
do
{
for(;;)
{
……
}while();
}
[例3-15]打印8行7列的星形矩陣。
流程圖見圖3-12,程序如下:
#include<stdio.h>
main()
{
inti,j;
for(i=0;i<8,i++)/*控制行*/
{
for(j=0;j<7>;j++)/*控制列*/
printf(“*”);
printf(“n”);/*換行*/
}
}
打印結(jié)果如下:
RUN
*******
*******
*******
*******
*******
*******
*******
*******
將程序中for(j=0;j<7;j++)改為for(j=0;j<i;j++),用行數(shù)來控制每行星號(hào)的多少,就可以
打印三角形。
3.4.4break與continue語句
有時(shí),我們需要在循環(huán)體中提前跳出循環(huán),或者在滿足某種條件下,不執(zhí)行循環(huán)中剩下
的語句而立即從頭開始新的一輪循環(huán),這時(shí)就要用到break和continue語句。
1.break語句
在前面學(xué)習(xí)switch語句時(shí),我們已經(jīng)接觸到break語句,在case子句執(zhí)行完后,通過break
語句使控制立即跳出switch結(jié)構(gòu)。在循環(huán)語句中,break語句的作用是在循環(huán)體中測(cè)試到應(yīng)立
即結(jié)束循環(huán)時(shí),使控制立即跳出循環(huán)結(jié)構(gòu),轉(zhuǎn)而執(zhí)行循環(huán)語句后的語句。
[例3-16]打印半徑為1到10的圓的面積,若面積超過100,則不予打印。
#include<stdio.h>
main()
{
intr;
float area;
for(r=1;r<=10;r++)
{
area=3.141593*r*r;
if(area>100.0)
break;
printf(“square=%fn”,area);
}
printf(“nowr=%dn”,r);
}運(yùn)行程序:
RUN
square=3.141593
square=12.566373
square=28.274338
square=50.265488
square=78.539825
nowr=6
當(dāng)break處于嵌套結(jié)構(gòu)中時(shí),它將只跳出最內(nèi)層結(jié)構(gòu),而對(duì)外層結(jié)構(gòu)無影響。
2.continue語句
continue語句只能用于循環(huán)結(jié)構(gòu)中,一旦執(zhí)行了continue語句,程序就跳過循環(huán)體中位于
該語句后的所有語句,提前結(jié)束本次循環(huán)周期并開始新一輪循環(huán)。
[例3-17]計(jì)算半徑為1到15的圓的面積,僅打印出超過50的圓面積。
#include<stdio.h>
main()
{
int r;
float area;
for(r=1;r<=5;r++)
{
area=3.141593*r*r;
if(area<50.0)
continue;
printf(“square=%f”,area);
}
}
結(jié)果為:
RUN
square=50.265488
square=78.539825
同break一樣,continue語句也僅僅影響該語句本身所處的循環(huán)層,而對(duì)外層循環(huán)沒有影
響。
3.4.5程序應(yīng)用舉例
[例3-18]驗(yàn)證哥德巴赫猜想:任一充分大的偶數(shù),可以用兩個(gè)素?cái)?shù)之和表示,例如:
4=2+2
6=3+3
……
98=19+79
哥德巴赫猜想是世界著名的數(shù)學(xué)難題,至今未能在理論上得到證明,自從計(jì)算機(jī)出現(xiàn)后,人們就開始用計(jì)算機(jī)去嘗試解各種各樣的數(shù)學(xué)難題,包括費(fèi)馬大定理、四色問題、哥德巴赫猜想等,雖然計(jì)算機(jī)無法從理論上嚴(yán)密地證明它們,而只能在很有限的范圍內(nèi)對(duì)其進(jìn)行檢驗(yàn),但也不失其意義。費(fèi)馬大定理已于1994年得到證明,而哥德巴赫猜想這枚數(shù)學(xué)王冠上的寶石,至今無人能及。
分析:我們先不考慮怎樣判斷一個(gè)數(shù)是否為素?cái)?shù),而從整體上對(duì)這個(gè)問題進(jìn)行考慮,可
以這樣做:讀入一個(gè)偶數(shù)n,將它分成p和q,使n=p+q。怎樣分呢?可以令p從2開始,每次加1,而令q=n-p,如果p、q均為素?cái)?shù),則正為所求,否則令p=p+q再試。
其基本算法如下:
1)讀入大于3的偶數(shù)n。
2)P=1
3)do{
4)p=p+1;q=n-p;
5)p是素?cái)?shù)嗎?
6)q是素?cái)?shù)嗎?
7)}whilep、q有一個(gè)不是素?cái)?shù)。
8)輸出n=p+q。
為了判明p、q是否是素?cái)?shù),我們?cè)O(shè)置兩個(gè)標(biāo)志量flag p和flag q,初始值為0,若p是素?cái)?shù),
令flag p=1,若q是素?cái)?shù),令flag q=1,于是第7步變成:
7)}while(flag p*flag q==0);
再來分析第5、第6步,怎樣判斷一個(gè)數(shù)是不是素?cái)?shù)呢?
素?cái)?shù)就是除了1和它自身外,不能被任何數(shù)整除的整數(shù),由定義可知:
2、3、5、7、11、13、17、19等是素?cái)?shù);
1、4、6、8、9、10、12、14等不是素?cái)?shù);
要判斷i是否是素?cái)?shù),最簡(jiǎn)單的辦法是用2、3、4、??i-1這些數(shù)依次去除i,看能否除盡,
若被其中之一除盡,則i不是素?cái)?shù),反之,i是素?cái)?shù)。
但其實(shí),沒必要用那么多的數(shù)去除,實(shí)際上,用反證法很容易證明,如果小于等于i的平
方根的數(shù)都除不盡,則i必是素?cái)?shù)。于是,上述算法中的第5步、第6步可以細(xì)化為:
第5)步p是素?cái)?shù)嗎?
flag p=1;
for(j=2;j<=[sqrt(p)];j++)
ifp除以j的余數(shù)=0
{flag p=0;
break;}
第6)步q是素?cái)?shù)嗎?
flag q=1;
for(j=2;j<=[sqrt(q)];j++)
ifq除以j的余數(shù)=0
{flag q=0;
break;}
程序如下:
#include<math.h>
#include<stdio.h>
main()
{
intj,n,p,q,flag p,flag q;
printf(“please input n:”);
scanf(“%d”,&n);
if(((n%2)!=0)||(n<=4))
printf(“inputdataerror!n”);
else
{
p=1;
do{
p=p+1;
q=n-p;
flag p=1;
for(j=2;j<=(int)(floor(sqrt((double)(p))));j++)
{
if((p%j)==0)
{
flag p=0;
break;
}
}
flag q=1;
for(j=2;j<=(int)(floor(sqrt((double)(q))));j++)
{
if((q%j)==0)
{
flag q=0;
break;
}
}
}while(flag p*flag q==0);
printf(“%d=%d+%dn,”n,p,q);
}
}
程序運(yùn)行結(jié)果如下:
RUN?
please input n:8
8=3+5
RUN
please input n:98
98=19+79
RUN
please input n:9
input data error!