版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第5章函數(shù)
5.1函數(shù)的定義5.2函數(shù)的調(diào)用5.3函數(shù)原型及函數(shù)聲明5.4數(shù)據(jù)存儲類5.5多文件程序中函數(shù)和變量的處理5.6遞歸5.7迭代5.8系統(tǒng)庫函數(shù)習(xí)題55.1函數(shù)的定義在C語言中的函數(shù)分為兩大類:一類是程序員定義的函數(shù),另一類是C標(biāo)準(zhǔn)庫中預(yù)定義的函數(shù)。
C標(biāo)準(zhǔn)庫提供了豐富的函數(shù)集,這些函數(shù)集中的函數(shù)能完成常用的數(shù)學(xué)計(jì)算、字符串操作、字符操作、輸入/輸出等多種有用的操作。使用標(biāo)準(zhǔn)庫函數(shù)可以節(jié)省程序開發(fā)的時(shí)間,使程序具有更好的可移植性,因此應(yīng)盡量多地熟悉和掌握ANSIC中的標(biāo)準(zhǔn)庫函數(shù)。但現(xiàn)實(shí)世界是生動復(fù)雜的,單用庫函數(shù)不可能解決所有問題,程序員還必須根據(jù)情況自已定義能解決專門問題的新函數(shù),因此必須掌握函數(shù)的定義和使用方法。函數(shù)由函數(shù)頭和函數(shù)體兩部分組成,其格式如下:
〈返回值類型〉〈函數(shù)名〉(〈參數(shù)列表〉) {聲明部分 語句部分
}第一行為函數(shù)頭,其中的函數(shù)名可以是任何合法的標(biāo)識符,它最好能直觀地反映出該函數(shù)所完成的任務(wù),以增強(qiáng)程序的可讀性。返回值類型是返回給調(diào)用者結(jié)果的數(shù)據(jù)類型。如果不指定返回值類型,編譯器總假定返回的是int類型。即便如此,明顯地寫出返回int類型也是一種良好的習(xí)慣。參數(shù)列表是用逗號分開的參數(shù)說明,參數(shù)說明的形式是:
〈參數(shù)類型〉〈參數(shù)名〉對每一個(gè)參數(shù)都必須作這樣的說明,不能在一個(gè)類型名后跟多個(gè)參數(shù)名。函數(shù)體由大括號{}括起來,一般由兩部分組成:說明部分和語句部分。說明部分聲明用于函數(shù)內(nèi)部的臨時(shí)變量。也可以沒有說明部分,只有語句部分。例如,定義求兩個(gè)浮點(diǎn)數(shù)之和的函數(shù):
floatsum(floatx,floaty) {returnx+y; }函數(shù)的兩個(gè)參數(shù)x、y被定義成浮點(diǎn)型,返回值的類型也是浮點(diǎn)型。該函數(shù)體中沒有說明部分,只有語句部分。
【例5-1】編一個(gè)求整數(shù)位數(shù)的函數(shù)。intwsh(intn){inti;i=0;while(n){n/=10;i++;}returni;}函數(shù)wsh()有一個(gè)整型參數(shù)n,將返回這個(gè)整數(shù)n的位數(shù)。在該函數(shù)體中,i是在本函數(shù)內(nèi)部定義的中間變量。定義函數(shù)時(shí)系統(tǒng)并不為中間變量分配內(nèi)存空間,只有在函數(shù)被調(diào)用時(shí),才為其分配內(nèi)存,函數(shù)調(diào)用一結(jié)束,該內(nèi)存即被收回。因此,在函數(shù)wsh()的外部不可以對變量i加以引用。函數(shù)不一定要有返回值,也可以用來完成某些特定的操作。
【例5-2】編一個(gè)函數(shù),它能輸出由字母組成的平行四邊形,即
A
B
C
D B
C
D
E
C
D
E
F D
E
F
G編程思路:把要輸出的行數(shù)n作為函數(shù)的參數(shù)。在輸出的每一行中要先輸出一定數(shù)量的空格,然后輸出n個(gè)連續(xù)的字母,共輸出n行。每行行首的空格數(shù)隨行數(shù)而增加。函數(shù)定義如下:voidprintnc(intn){inti,j;charc,d;c=d=′A′;for(i=1;i<=n;i++){for(j=1;j<i;j++)putchar(′└┘′);for(j=1;j<=n;j++){putchar(c++); putchar(′└┘′);}putchar(′\n′);c=++d;}}函數(shù)的返回值類型void指該函數(shù)不返回?cái)?shù)據(jù),僅執(zhí)行打印平行四邊形的操作。5.2函數(shù)的調(diào)用5.2.1函數(shù)的參數(shù)傳遞定義函數(shù)時(shí),在函數(shù)頭中出現(xiàn)的參數(shù)是形式參數(shù),簡稱形參。函數(shù)調(diào)用時(shí),主調(diào)函數(shù)應(yīng)當(dāng)根據(jù)被調(diào)函數(shù)形參的要求提供相應(yīng)的真實(shí)數(shù)據(jù),這稱為實(shí)在參數(shù),簡稱實(shí)參。實(shí)參和形參要做到個(gè)數(shù)相等,類型對應(yīng)一致。盡管實(shí)參和形參可以同名,但為了避免混淆,建議不要使用完全相同的實(shí)參和形參名。例如,調(diào)用上面定義的sum函數(shù)。#include<stdio.h>main(){floatf1,f2;scanf(″%f%f″,&f1,&f2);printf(″%f\n″,sum(f1,f2));return0;}這里main函數(shù)是主調(diào)函數(shù),sum函數(shù)是被調(diào)函數(shù),f1,f2是實(shí)參。main函數(shù)把f1,f2分別交給sum的x和y,sum函數(shù)開始工作,把x與y(即f1和f2)的和返回給main函數(shù),并作為printf函數(shù)的參數(shù)打印出來。
C語言中參數(shù)傳送的機(jī)理是值傳送,即主調(diào)函數(shù)把實(shí)在參數(shù)的拷貝傳給形參后即與形參脫離關(guān)系,函數(shù)體中對形參的任何處理都已和實(shí)參無關(guān)了。為了說明這一點(diǎn),請看下面關(guān)于交換兩個(gè)變量值的函數(shù)。
【例5-3】交換兩個(gè)變量值的函數(shù)。voidexchange(inti,intj){intk;printf(″i=%d,j=%d\n″,i,j);k=i;i=j;j=k;printf(″i=%d,j=%d\n″,i,j);}#include<stdio.h>main(){intm=1,n=10;printf(″m=%d,n=%d\n″,m,n);exchange(m,n);
printf(″m=%d,n=%d\n″,m,n);return0;}運(yùn)行輸出:m=1,n=10(函數(shù)調(diào)用前)i=1,j=10 (函數(shù)中參數(shù)交換前)i=10,j=1 (函數(shù)中參數(shù)交換后)m=1,n=10 (函數(shù)調(diào)用后)可以看出,在exchange函數(shù)調(diào)用前后主函數(shù)中的變量m和n的值沒有改變。這說明雖然在函數(shù)中發(fā)生了參數(shù)的交換,但影響不到主調(diào)函數(shù)。該程序的函數(shù)調(diào)用示意圖如圖5-1所示。①k=i;②i=j;③j=k;圖5-1例5-3的調(diào)用示意圖調(diào)用時(shí),m、n把值傳給i、j后即與之脫離關(guān)系,函數(shù)中i、j的交換對m、n沒有影響。如何讓函數(shù)實(shí)現(xiàn)實(shí)參的交換呢?在學(xué)習(xí)了指針類型之后,這個(gè)問題就可以解決了。5.2.2函數(shù)的返回值函數(shù)的返回值是由return語句實(shí)現(xiàn)的。對于任何函數(shù),只要它的返回值類型不是void,那么它都要有個(gè)返回值。void是空類型,表示函數(shù)不返回值。通過return語句,被調(diào)函數(shù)將控制權(quán)及數(shù)值交給主調(diào)函數(shù),返回到調(diào)用處。函數(shù)調(diào)用過程如圖5-2所示。圖5-2函數(shù)調(diào)用過程示意圖main() floatsum(floatx,floaty){ { returnx+y;printf(″…″,sum(f1,f2)); }}被調(diào)函數(shù)返回的數(shù)值類型如何決定呢?如果return語句的表達(dá)式類型和函數(shù)頭部返回值類型不一致時(shí),應(yīng)該返回哪一個(gè)類型呢?答案是應(yīng)該返回函數(shù)頭部說明的類型。比如,若sum函數(shù)的定義作如下修改:intsum(floatx,floaty){returnx+y;}這時(shí)主調(diào)函數(shù)中接收的應(yīng)該是int類型,因此printf函數(shù)就應(yīng)該改成下面的形式:
printf(″%d\n″,sum(f1,f2);即輸出控制符由′%f′改為′%d′。既然類型為非void的函數(shù)都要返回值,main也是個(gè)函數(shù),那么它返回一個(gè)什么值,又返回給誰呢?如果main函數(shù)名前面沒有返回類型,則隱含是int類型,所以也應(yīng)當(dāng)返回一個(gè)整數(shù),不管什么整數(shù)都行,它只是表示把控制權(quán)返回給調(diào)用者,而其具體值并無太大的意義,這就是為什么我們都要在主函數(shù)main的最后寫上return0的原因。0表示成功返回。主函數(shù)的控制權(quán)最后返回到操作系統(tǒng)。5.2.3函數(shù)的調(diào)用方式按被調(diào)函數(shù)在主調(diào)函數(shù)中的位置,可以有以下幾種調(diào)用方式。
1.被調(diào)函數(shù)作為函數(shù)語句單獨(dú)出現(xiàn)一個(gè)函數(shù)可看作是一個(gè)函數(shù)表達(dá)式,在其后面加分號即構(gòu)成函數(shù)語句,例如我們經(jīng)常使用的輸入/輸出函數(shù)
printf(″a=%d,b=%d\n″,a,b);就是一個(gè)函數(shù)調(diào)用語句。前面例子中的
exchange(m,n);也是一個(gè)函數(shù)調(diào)用語句。
2.被調(diào)函數(shù)作為另一個(gè)表達(dá)式的一部分例如,求兩個(gè)數(shù)的平均值,可調(diào)用函數(shù)sum: moyen=sum(m,n)/2;
3.被調(diào)函數(shù)作為另外一個(gè)函數(shù)的參數(shù)例如,輸出兩個(gè)數(shù)的和:
printf(″sum=%f\n″,sum(a,b));如輸出四個(gè)數(shù)a、b、c、d的和,可寫為:
printf(″sum4=%f\n″,sum(sum(a,b),sum(c,d));在被調(diào)函數(shù)中含有返回值的return語句,如果在主調(diào)函數(shù)中把被調(diào)函數(shù)作為表達(dá)式的一部分,則此時(shí)被調(diào)函數(shù)的返回值是有意義的;而如果把被調(diào)函數(shù)作為過程語句處理,則主調(diào)函數(shù)對被調(diào)函數(shù)的返回值不予理睬,只是把控制權(quán)收回來,被調(diào)函數(shù)的返回值被舍棄。
【例5-4】把用公式求π近似值的計(jì)算編成函數(shù),然后調(diào)用之。計(jì)算中的項(xiàng)數(shù)由用戶決定。
#include<stdio.h>
doublepi(intt) { inti;
doubles=1.0,sum=0,item=1; for(i=1;i<=t;i++) {sum+=item; s=-s; item=s/(2*i+1); }sum=sum*4;printf(″PI=%f\n″,sum);returnsum;}main(){intn;printf(″Inputtheitemnumber:\n″);
scanf(″%d″,&n);pi(n);return0;}運(yùn)行輸出:Inputtheitemnumber:100↙
PI=3.131593再運(yùn)行:Inputtheitemnumber:1000↙
PI=3.141493主調(diào)函數(shù)是將函數(shù)pi(n)作為過程語句調(diào)用的,因此它沒有接收pi()函數(shù)的返回值,函數(shù)中求得的值已在函數(shù)本身輸出了。若在主調(diào)函數(shù)中對pi的調(diào)用換一種形式,比如: printf(″pi=%f\n″,pi(n));則pi的返回值就有用了。
【例5-5】求奇特?cái)?shù)。輸入兩個(gè)整數(shù)a、b,計(jì)算并求出一個(gè)整數(shù)x,使x+a和x+b都是完全平方數(shù)。如在某個(gè)范圍內(nèi)找不到這樣的x,則說明對這一組a、b不存在奇特?cái)?shù),需再輸入一組a、b。編程思路:首先根據(jù)題意,列出方程:這里已假定x+a是個(gè)完全平方數(shù),接著需要驗(yàn)證z也是個(gè)完全平方數(shù),如驗(yàn)證成功,則說明這樣的奇特?cái)?shù)已經(jīng)找到,并進(jìn)一步給出z是哪個(gè)數(shù)的平方??闪睥偈街械膟從1開始逐一增加,通過公式x=y2-a不斷地求出x,然后把它代入②式中求出z,再來判斷z是否為完全平方數(shù)。判斷完全平方數(shù)的運(yùn)算可用一個(gè)函數(shù)來表示。判斷時(shí)采用下面的方法。根據(jù)公式:1+3+5+7+…+(2n-1)=n2
可令x=n2,從x中不斷地減去1,3,5,…,最后一定為0,此時(shí)函數(shù)返回值為1,說明x是一個(gè)完全平方數(shù)。如果連續(xù)減的最后結(jié)果不為0,則說明x不是一個(gè)完全平方數(shù),返回值為0。程序如下:#include<stdio.h>#defineMAX10000intsquareful(longintx){inti;for(i=1;;i+=2){if((x-=i)<0) break;if(x==0)return1;}return0;}main(){inti,j;longinta,b,x,y,z;do{printf(″Inputa,b:\n″);scanf(″%ld%ld″,&a,&b);
for(y=1;;++y){x=y*y-a;if(x<0)continue;
z=x+b;if(squareful(z)==1)gotolabl;if(y>MAX){printf(″change(a,b)!\n″); break;}}}while(y>MAX);labl:printf(″x=%ld\n″,x);printf(″%ld+%ld=%ld**2\n″,x,a,y);for(j=1,i=1;;i+=2)if((z-=i)!=0)j++;elsebreak;printf(″%ld+%ld=%d**2\n″,x,b,j);return0;}運(yùn)行輸出:Inputa,b:100150↙
change(a,b)!Inputa,b:100200↙
x=476476+100=24**2476+200=26**2在主調(diào)函數(shù)中調(diào)用了判斷完全平方數(shù)的函數(shù)squareful,若該函數(shù)值為1,說明已找到一個(gè)合乎要求的x,則通過goto語句轉(zhuǎn)到輸出這個(gè)x的語句。如果y>MAX,說明在MAX范圍內(nèi)無這種奇特?cái)?shù),則通過break語句跳出for循環(huán),再在外層的do_while中輸入新一組(a,b)的值。在已確定z是一個(gè)完全平方數(shù)后,進(jìn)一步還要確定z是哪一個(gè)數(shù)的平方,這仍然采用不斷從z中減去1,3,5…直到最后結(jié)果為0的方法,減一次后計(jì)數(shù)器j加1,結(jié)果為0時(shí)的j就是所求的結(jié)果,即j2=z。因j定義為int類型,所以輸出時(shí)使用控制符“%d”,而不是“%ld”。
【例5-6】任何一個(gè)非素?cái)?shù),都可以表示成多個(gè)素?cái)?shù)之積的形式。#include<stdio.h>#include<math.h>voidf(intn){intk,r;printf(″%d=″,n);for(k=2;k<=sqrt(n);k++){r=n%k; while(r==0) {printf(″%d″,k); n/=k; if(n>1)printf(″*″); r=n%k;}}if(n!=1)printf(″%d\n″,n);}main(){intm;printf(″\n″);scanf(″%d″,&m);f(m);return0;}運(yùn)行結(jié)果:
125↙ 125=5*5*5再運(yùn)行:
348↙ 348=2*2*3*29再運(yùn)行:
347↙
347=347 //347本身是一個(gè)素?cái)?shù)
4.函數(shù)的嵌套調(diào)用
C語言中不能定義嵌套函數(shù),但可以嵌套調(diào)用,嵌套調(diào)用的示意圖見圖5-3。執(zhí)行過程是沿著①、②、③、④、⑤、⑥、⑦、⑧、⑨的路線來進(jìn)行的,程序始于main()函數(shù),終于main()函數(shù)。
【例5-7】輸入一個(gè)整數(shù),輸出其平方與立方。圖5-3嵌套調(diào)用的示意圖我們把求平方和立方的計(jì)算編成函數(shù):/*calculatesquare*/longsq(longinti){longa;a=i*i;returna;}/*calculatecube*/longcub(longintj){longb;b=sq(j)*j;returnb;}#include<stdio.h>main(){longintn;printf(″Inputn=?\n″);scanf(″%ld″,&n);printf(″squareof%ldis%ld\n″,n,sq(n));printf(″Cubeof%ldis%ld\n″,n,cub(n));return0;}運(yùn)行輸出:Inputn=?3↙
squareof3is9Cubeof3is27程序中,主函數(shù)調(diào)用cub函數(shù),而cub函數(shù)又調(diào)用sq函數(shù),構(gòu)成嵌套調(diào)用。
【例5-8】用弦截法求方程x3-5x2+16x-80=0的根。
編程思路:設(shè)f(x)=x3-5x2+16x-80,其曲線如圖5-4所示,讀入兩個(gè)數(shù)x1和x2,其對應(yīng)的函數(shù)值為f(x1)和f(x2),通過這兩點(diǎn)作弦交X軸于x,則x距解x0的距離比x1和x2距x0的距離更近;而如果再通過f(x)和f(x2)作一條弦,則該弦交X軸的點(diǎn)會距x0更近……這樣可以一步步地逼近x0。圖5-4方程求解示意圖在讀入x1和x2時(shí)應(yīng)保證方程的根x0在它們之間,如不在它們之間就再讀入兩個(gè)新的x1和x2,反復(fù)進(jìn)行直到符合要求為止。根在x1和x2之間的條件是f(x1)·f(x2)<0,即f(x1)和f(x2)異號。為求弦和X軸的交點(diǎn),可利用三角形的相似關(guān)系,如在圖5-4中有Δxx1f(x1)~Δxx2f(x2)又由于|f(x1)|=-f(x1),則比例式為由此公式求出x:進(jìn)而可求出x處的函數(shù)值f(x)。x趨于x0的標(biāo)志是|f(x)|趨于0,所以可以把|f(x)|<ε作為循環(huán)的終止條件。為求解這一問題,我們可以定義下列三個(gè)函數(shù):①表示f(x)的函數(shù):給定一個(gè)x作參數(shù),返回f(x)的函數(shù)值。②求交點(diǎn)的函數(shù):給定x1、x2的參數(shù),返回弦與x軸的交點(diǎn)。③求根的函數(shù):給定x1和x2作參數(shù),返回方程符合條件的根。問題的流程圖如圖5-5所示。圖5-5問題的流程圖程序如下:#include<stdio.h>#include<math.h>#defineEPS1.e-3floatf(floatx){floaty;y=((x-5.0)*x+16.0)*x-80; /*把多項(xiàng)式寫成乘、加形式,以提高效率*/returny;}floatxpoint(floatx1,floatx2){floatx;x=(x1*f(x2)-x2*f(x1))/(f(x2)-f(x1));/*調(diào)用了函數(shù)f*/returnx;}floatroot(floatx1,floatx2){floatx,y,y1;y1=f(x1);
do{x=xpoint(x1,x2);y=f(x);if(y*y1>0) /*說明根在后半部*/{y1=y;x1=x;}elsex2=x;/*根在前半部*/}while(fabs(y)>=EPS);returnx;}main(){floatx1,x2,f1,f2,x;do{printf(″Inputx1,x2:\n″);
scanf(″%f%f″,&x1,&x2);
f1=f(x1);f2=f(x2);}while(f1*f2>=0);x=root(x1,x2);printf(″Theroot=%8.4f\n″,x);return0;}運(yùn)行輸出:Inputx1,x2:28↙
Theroot=└┘└┘5.0000在程序中,主函數(shù)main調(diào)用了root函數(shù),root調(diào)用了xpoint函數(shù),xpoint調(diào)用了f函數(shù),其調(diào)用關(guān)系如圖5-6所示。圖5-6調(diào)用關(guān)系圖上面用流程圖描述了執(zhí)行過程,如果用N-S圖來描述,則應(yīng)注意N-S圖中表達(dá)do_while的條件表達(dá)式的方法。圖5-7為用N-S圖描述的執(zhí)行過程。圖5-7N-S圖描述5.3函數(shù)原型及函數(shù)聲明在上面的函數(shù)調(diào)用中,我們有意把被調(diào)函數(shù)都放在了主調(diào)函數(shù)的前面,為的是在調(diào)用時(shí),被調(diào)函數(shù)的一切信息對主調(diào)函數(shù)來說都是已知的,因?yàn)榫幾g程序是按文本出現(xiàn)的先后次序?qū)Τ绦蜻M(jìn)行編譯的。如果被調(diào)函數(shù)在后,主調(diào)函數(shù)在前,則應(yīng)如何處理呢?這時(shí)應(yīng)該在主調(diào)函數(shù)之前或在主調(diào)函數(shù)中對被調(diào)函數(shù)進(jìn)行聲明。函數(shù)聲明的一般形式是:
〈返回值類型〉〈函數(shù)名〉(〈參數(shù)類型聲明表〉);其中,〈參數(shù)類型聲明表〉的形式是:
〈參數(shù)類型〉[參數(shù)名]],〈參數(shù)類型〉[參數(shù)名]]…方括號中的內(nèi)容可以不要,省略號表示可以重復(fù)。這就是說在函數(shù)聲明時(shí),只列出參數(shù)類型就可以了,不必再寫出參數(shù)名,寫出是為了清楚,但編譯程序會將它忽略。例如,對求和函數(shù)sum就可作以下的聲明:main(){floatsum(float,float);
…sum();
}floatsum(floatx,floaty){…}如果被調(diào)函數(shù)在后而又未在主調(diào)函數(shù)中聲明,則編譯時(shí)會給出錯誤提示:
″Function′xx′shouldhaveaprototype″(“函數(shù)xx應(yīng)當(dāng)有個(gè)原型”)函數(shù)的聲明就是函數(shù)原型的聲明。函數(shù)原型提供函數(shù)如下的信息:
(1)函數(shù)返回值的類型。
(2)函數(shù)的參數(shù)個(gè)數(shù)。
(3)各個(gè)參數(shù)的類型。
(4)各參數(shù)之間的順序。編譯器就利用函數(shù)原型來檢驗(yàn)函數(shù)調(diào)用,如調(diào)用和原型不一致就會給出錯誤提示,這樣可以避免不經(jīng)檢驗(yàn)而執(zhí)行該函數(shù)時(shí)可能導(dǎo)致的致命錯誤或邏輯錯誤。函數(shù)原型的聲明地點(diǎn),可以在所有函數(shù)之外,也可以在某個(gè)函數(shù)之中。如在函數(shù)之外,則處在其聲明之下的所有函數(shù)都可以引用它;若在函數(shù)之中,則只有本函數(shù)可以引用它。
【例5-9】通過函數(shù)調(diào)用,求三個(gè)正整數(shù)的最小公倍數(shù)。
編程思路:如果三個(gè)數(shù)中最大數(shù)的倍數(shù)能被這三個(gè)數(shù)整除,則這個(gè)倍數(shù)就是它們的最小公倍數(shù)。先求出三個(gè)數(shù)的最大值,對這個(gè)最大值的倍數(shù)循環(huán)測試,看能否被這三個(gè)數(shù)整除。從最小的倍數(shù)開始測試,則遇到的第一個(gè)符合條件的倍數(shù)就是它們的最小公倍數(shù)。把求三個(gè)數(shù)最大值的計(jì)算編為一個(gè)獨(dú)立的函數(shù),設(shè)該函數(shù)的定義在主調(diào)函數(shù)之后,則在主調(diào)函數(shù)前應(yīng)對該函數(shù)作原型聲明。程序如下:#include<stdio.h>longmax3(long,long,long);main(){longa,b,c,i=1,j,m;printf(″Input3integers:\n″);scanf(″%ld%ld%ld″,&a,&b,&c);m=max3(a,b,c);while(1){j=m*i;if(j%a==0&&j%b==0&&j%c==0)break;i++;}printf(″Result=%ld\n″,j);return0;}longmax3(longx1,longx2,longx3){longmax;if(x1>x2)max=x1;elsemax=x2;if(max<x3)max=x3;returnmax;}運(yùn)行輸出:Input3integers:123236369↙
Result=87084主函數(shù)中while(1)表示循環(huán)條件永遠(yuǎn)為真,但能否繼續(xù)循環(huán)下去,還要看循環(huán)體中的if條件是否滿足,一旦條件滿足,則執(zhí)行break語句退出循環(huán),此時(shí)最大數(shù)的倍數(shù)j即為所求結(jié)果。
【例5-10】如果一個(gè)整數(shù)的所有因子(因子從1開始,不含自身)之和等于其自身,則這個(gè)整數(shù)就被稱為是完數(shù)。數(shù)學(xué)中有一個(gè)定理稱,如果2n+1-1是一個(gè)素?cái)?shù),則2n×(2n+1-1)就一定是完數(shù)。編程求10000之內(nèi)的所有完數(shù)。
編程思路:對10000之內(nèi)所有由2n+1-1生成的整數(shù)進(jìn)行是否為素?cái)?shù)的判斷,如其為素?cái)?shù),則用2n×(2n+1-1)生成的數(shù)就是相應(yīng)的完數(shù)。對每一個(gè)完數(shù)還應(yīng)求出它的所有因子。把判斷素?cái)?shù)及求因子的操作分別設(shè)計(jì)為兩個(gè)函數(shù)。設(shè)函數(shù)定義在后,則在主函數(shù)中應(yīng)對它們進(jìn)行原型聲明。程序如下:#include<stdio.h>#include<math.h>main(){intprime(int);voidfactor(intn);intw,m,n=1;m=pow(2,n+1)-1;w=m*pow(2,n);while(w<10000){if(prime(m)){ printf(″%disaperfectnumber\n″,w); printf(″%d=pow(2,%d)*(pow(2,%d+1)-1)n=%d\n″,w,n,n,n); factor(w);}n++;m=pow(2,n+1)-1;w=m*pow(2,n);}printf(″\n\n″);return0;}intprime(intn){inti,q,flag=1;q=sqrt(n);for(i=2;i<=q;i++) if(n%i==0) flag=0;if(flag==1) return1;else return0;}voidfactor(intn){inti,m;m=n/2;printf(″%d=1″,n);for(i=2;i<=m;i++)if(n%i==0&&n!=0){ putchar(′+′); printf(″%d″,i);}printf(″\n″);}運(yùn)行輸出:6isaperfectnumber6=pow(2,1)*(pow(2,1+1)-1)n=16=1+2+328isaperfectnumber28=pow(2,2)*(pow(2,2+1)-1)n=228=1+2+4+7+14496isaperfectnumber496=pow(2,4)*(pow(2,4+1)-1)n=4496=1+2+4+8+16+31+62+124+2488128isaperfectnumber8128=pow(2,6)*(pow(2,6+1)-1)n=68128=1+2+4+8+16+32+64+127+254+508+1016+2032+4064函數(shù)原型的一個(gè)重要特點(diǎn)是強(qiáng)制類型轉(zhuǎn)換,即把實(shí)參的類型強(qiáng)制轉(zhuǎn)換成形參的類型,就好像是實(shí)參把其值直接賦給形參類型的變量一樣。比如數(shù)學(xué)庫函數(shù)sqrt的函數(shù)原型中指定參數(shù)為double類型,但如用整型數(shù)據(jù)調(diào)用該函數(shù)時(shí)仍能正確運(yùn)行。語句
printf(″%.3f\n″,sqrt(4));能正確計(jì)算sqrt(4)并打印出值2.000。函數(shù)原型通知編譯器在把整數(shù)4傳遞給sqrt之前先轉(zhuǎn)換成4.0。通常情況下,在函數(shù)調(diào)用前,與原型中參數(shù)類型不完全一致的實(shí)參會按照“提升規(guī)則”轉(zhuǎn)換為合適的類型。提升規(guī)則把存儲空間較小的類型轉(zhuǎn)換成存儲空間較大的類型,實(shí)際上是建立該值的臨時(shí)值來使用,原始值并沒有被修改。提升規(guī)則說明了在不丟失精度的情況下把一種類型轉(zhuǎn)換為其他類型。反之,相反的調(diào)用會產(chǎn)生不正確的結(jié)果。例如,求整數(shù)平方的函數(shù)原型為:
intsquare(int);用浮點(diǎn)型實(shí)參調(diào)用就會返回不正確的結(jié)果,如square(4.5),則返回16而非20.25。因此應(yīng)該避免與提升規(guī)則不一致的逆向調(diào)用。5.4數(shù)據(jù)存儲類當(dāng)用戶編程上機(jī)的時(shí)候,編譯器會為用戶提供一定的內(nèi)存空間讓用戶使用。這個(gè)內(nèi)存空間被劃分為不同的區(qū)域以存放不同的數(shù)據(jù)。大體上用戶區(qū)劃分為三部分(如圖5-8所示):程序區(qū),用來存放用戶的程序;動態(tài)區(qū),用來存放暫時(shí)的數(shù)據(jù);靜態(tài)區(qū),用來存放相對永久的數(shù)據(jù)。
C語言中的變量都有兩個(gè)特征:一個(gè)是它們的作用范圍有大有?。灰粋€(gè)是它們的存在期限有長有短。這兩個(gè)特征統(tǒng)一于它們的存儲類,因此我們從存儲出發(fā)來研究變量的這兩方面的特征。程序區(qū)動態(tài)區(qū)靜態(tài)區(qū)圖5-8用戶區(qū)的劃分用戶區(qū)5.4.1自動(auto)變量自動變量的定義位置在復(fù)合語句內(nèi)部。復(fù)合語句可以嵌套,函數(shù)體就是個(gè)最大的復(fù)合語句。自動變量在定義時(shí)可以加前綴auto關(guān)鍵字或什么都不加。自動變量的作用范圍是從定義點(diǎn)起到復(fù)合語句的結(jié)束。例如下面的程序:#include<stdio.h>main(){autointi=1,j=2;printf(″i=%d,j=%d\n″,i,j);{ inti=3,a=4; floatx=1.1,y=2.2; printf(″i=%d,a=%d\n″,++i,a);
printf(″x=%f,y=%f\n″,x,y);
j++;}printf(″i=%d,j=%d\n″,i,j);return0;}程序中,i、j、x、y、a都是自動變量,只是x、y和a的作用范圍是在內(nèi)嵌的復(fù)合語句中,而j的作用范圍是整個(gè)函數(shù)體,其作用力可貫穿內(nèi)嵌的復(fù)合語句。注意變量i,在兩個(gè)地方都有定義,但它們的作用范圍不同:內(nèi)嵌復(fù)合語句中的i只在此復(fù)合語句中起作用;而外部的i在整個(gè)函數(shù)中起作用,但因被內(nèi)嵌的復(fù)合語句中的i所屏蔽,所以其作用達(dá)不到復(fù)合語句內(nèi)部。因此這兩個(gè)i是不同的變量,雖然它們的名字是一樣的。所以運(yùn)行上面的程序,輸出結(jié)果是:i=1,j=2i=4,a=4x=1.100000,y=2.200000i=1,j=3自動變量放在動態(tài)區(qū)中,函數(shù)調(diào)用結(jié)束后就被撤銷。5.4.2寄存器(register)變量用戶頻繁使用的數(shù)據(jù)不一定全要放在內(nèi)存中,還可以放在運(yùn)算器的寄存器中。如果數(shù)據(jù)放在內(nèi)存中,運(yùn)算器就要到內(nèi)存中取數(shù)據(jù),運(yùn)算結(jié)束后還要送回內(nèi)存,這樣就需要反復(fù)地訪問內(nèi)存,影響運(yùn)算速度。若把數(shù)據(jù)直接放在運(yùn)算器的寄存器中,則可免除頻繁訪問內(nèi)存之勞,提高執(zhí)行效率。定義寄存器變量時(shí)前面要加關(guān)鍵字register,如:
registerintri,rj;注意:①寄存器變量的個(gè)數(shù)是有限的,并且各個(gè)系統(tǒng)都不相同。如寄存器變量的個(gè)數(shù)超過系統(tǒng)提供的數(shù)量,則多出來的變量會被作為自動變量處理。②對于有些系統(tǒng),如TurboC,會把寄存器變量均作為自動變量處理,所以定義register變量意義不大。5.4.3靜態(tài)(static)變量如果定義變量時(shí)在前面加上static修飾,則這樣的變量就是靜態(tài)變量,它被放在內(nèi)存的靜態(tài)區(qū)。靜態(tài)變量的使用特征是:在定義時(shí)賦初值一次,如不顯式賦初值,則系統(tǒng)會對其自動賦初值(數(shù)值型變量初值為0,字符型變量初值為空字符′\0′)。包含static變量的函數(shù)被調(diào)用后,static變量的值并不消失,當(dāng)再次調(diào)用該函數(shù)時(shí),上次調(diào)用的結(jié)果就作為本次的初值使用。在main函數(shù)中定義static變量的意義不大,因?yàn)槌绦蛎看芜\(yùn)行都是重新分配空間的。
【例5-11】觀察static變量和自動變量的區(qū)別。#include<stdio.h>main(){inti;voidaust();for(i=0;i<5;i++)aust();return0;}voidaust(){intau=0;staticintst=0;
printf(″au=%d,st=%d\n″,au,st);++au;++st;}運(yùn)行輸出:au=0,st=0au=0,st=1au=0,st=2au=0,st=3au=0,st=4在函數(shù)aust中,靜態(tài)變量st的初值為0,函數(shù)每調(diào)用一次,其值增1,并把結(jié)果保存到下次調(diào)用時(shí)。而自動變量au雖然在函數(shù)調(diào)用時(shí)其值也增1,但當(dāng)函數(shù)調(diào)用結(jié)束后它的值也就消失了。
【例5-12】求下列函數(shù)的執(zhí)行結(jié)果。#include<stdio.h>main(){inti,j;intf(int);i=f(3);j=f(5);printf(″i=%d,j=%d\n″,i,j);return0;}intf(intn){staticints=1;while(n)s*=n--;returns;}運(yùn)行輸出:
i=6,j=720函數(shù)f()的功能是求參數(shù)n的階乘,即f(n)=n!,main函數(shù)對它調(diào)用了兩次。經(jīng)過f(3)的調(diào)用,使static變量s的值變?yōu)?;當(dāng)?shù)诙握{(diào)用f(5)時(shí),s的值并不再是從初值1開始,而是從上次調(diào)用的結(jié)果開始,所以j的值應(yīng)該是3!*5!=720。
【5-13】輸出下面程序的執(zhí)行結(jié)果。#include<stdio.h>main(){inti,m=3;intf(int,int);for(i=1;i<=3;i++)printf(″%d.″,f(i,m));return0;}intf(intx,inty){staticinti=1,m=1;i+=m+2;m=i*x+y;returnm;}運(yùn)行輸出:
7.29.135.注意:這里主調(diào)函數(shù)和被調(diào)函數(shù)中出現(xiàn)了同名變量i和m,但它們是不同的變量。被調(diào)函數(shù)f()中的變量i和m都是局限于此函數(shù)中的靜態(tài)變量,每次調(diào)用后的結(jié)果都不一樣;而主函數(shù)main()中的變量i和m在作為參數(shù)傳遞給函數(shù)f()時(shí)就變成了x和y的值。5.4.4外部變量在所有函數(shù)之外定義的變量稱為外部變量,它們不為某個(gè)函數(shù)所專有,程序中所有函數(shù)都可以引用它們,因此外部變量也就是全局變量。外部變量放在靜態(tài)區(qū)中。外部變量的使用方法根據(jù)它所處位置的不同而不同:
(1)在外部變量自然作用范圍之內(nèi)的函數(shù)可以直接引用它們。自然作用范圍指從變量的定義點(diǎn)到文件結(jié)束。
(2)在自然作用范圍之外,即對全局變量的引用在前,而它的定義在后,則應(yīng)當(dāng)對它作extern說明。
【例5-14】#include<stdio.h>intmax(intx,inty){returnx>y?x:y;}main(){externinta,b;intmin(int,int);printf(″max=%d\n″,max(a,b));printf(″min=%d\n″,min(a,b));return0;}inta=8,b=5;intmin(inti,intj){returni<j?i:j;}運(yùn)行輸出:max=8min=5在main函數(shù)中引用a、b時(shí),a、b尚未定義,因此要做extern說明。為了避免忽略對全局變量的extern說明,一般總把全局變量放在程序的最前面,使所有函數(shù)都處在它的自然作用范圍之內(nèi),所有函數(shù)都可以在不做extern說明的情況下引用它們。使用全局變量的好處是可以實(shí)現(xiàn)數(shù)據(jù)在各函數(shù)之間的流通,能增加函數(shù)間數(shù)據(jù)聯(lián)系的渠道,便于寫出高效的程序。缺點(diǎn)是任何函數(shù)都可以對全局變量進(jìn)行修改,因此很難判斷它的當(dāng)前值是什么,容易出現(xiàn)錯誤,因此應(yīng)該慎重使用。
【例5-15】求調(diào)和級數(shù)的和,結(jié)果用分?jǐn)?shù)表示。編程思路:利用下式求兩個(gè)分?jǐn)?shù)之和:
把求兩個(gè)分?jǐn)?shù)之和的運(yùn)算讓函數(shù)addrat來完成。在求出一個(gè)分?jǐn)?shù)后,還要把它們化簡成真分?jǐn)?shù),這就要找出分子、分母的最大公因子,并用它對分?jǐn)?shù)進(jìn)行化簡,這個(gè)計(jì)算由函數(shù)lowterm完成。在整個(gè)操作中都是對分子、分母進(jìn)行的,若在各個(gè)函數(shù)中都定義兩個(gè)分子、分母變量,則互相傳遞時(shí)會很麻煩,因此可以考慮定義兩個(gè)全局變量num和den代表分子、分母,以它們作為數(shù)據(jù)紐帶把各個(gè)函數(shù)聯(lián)系起來。#include<stdio.h>intnum,den;main(){intn,nterm;voidaddrat(int,int);voidlowterm();printf(″Inputthenumberofterms\n″);scanf(″%d″,&n);if(n<=0)printf(″Baddata!\n″);elseif(n==1)printf(″1/1\n″);else{num=1;den=1;for(nterm=2;nterm<=n;nterm++) { addrat(1,nterm); lowterm(); printf(″%d/%d\n″,num,den); }}return0;}voidaddrat(inta,intb){num=num*b+a*den;den=den*b;}voidlowterm(){intnum1,den1,rem;num1=num;den1=den;while(den1!=0){rem=num1%den1;num1=den1;den1=rem;}if(num1>1){num=num/num1;den=den/num1;}}運(yùn)行輸出:Inputthenumberofterms-1↙
Baddata!再運(yùn)行一次:Inputthenumberofterms1↙
1/1再運(yùn)行一次:Inputthenumberofterms5↙
3/211/625/12137/60主函數(shù)只對全局變量num和den賦了初值1,并沒有其他明顯的操作,但最后的輸出卻是經(jīng)過運(yùn)算后的值,這主要是借助全局變量num和den在函數(shù)addrat和lowterm中進(jìn)行的,因此主函數(shù)顯得相當(dāng)簡潔。5.5多文件程序中函數(shù)和變量的處理
C語言中的函數(shù)定義不能嵌套,所以函數(shù)相互之間都是互為外部的,有時(shí)說內(nèi)部函數(shù)和外部函數(shù),這主要是針對某個(gè)文件而言的。這里需要對C程序的組成作個(gè)說明,如圖5-9所示。圖5-9C程序的組成圖5-9說明,一個(gè)大一點(diǎn)的C程序可以由多個(gè)文件組成,一個(gè)文件又可以包含多個(gè)函數(shù),包含main函數(shù)的文件是主文件,是程序的入口和出口。文件是一個(gè)獨(dú)立的編譯單位,而函數(shù)不是獨(dú)立的編譯單位。在一個(gè)文件中定義的外部變量和函數(shù)能否被其他文件所引用,是我們現(xiàn)在要討論的問題。這里有兩種情況:
(1)若一個(gè)文件中定義的外部變量和函數(shù)不允許其他文件引用,這時(shí)應(yīng)在函數(shù)名和變量名的前面加上關(guān)鍵字static。如:staticinta;staticfloatmax(floatx,floaty)這里static聲明就把a(bǔ)和max局限在它所定義的文件中,它們只能在這個(gè)文件中被使用,不允許其他文件使用,相對于這個(gè)文件來說,它們是內(nèi)部的。
(2)若一個(gè)文件中定義的函數(shù)和外部變量可以被其他文件引用,這時(shí)在函數(shù)名和外部變量名前不加static說明,但是凡引用它們的文件必須在各自的文件內(nèi)部對這些函數(shù)和變量作extern說明。比如,在文件f1.c中定義了一個(gè)外部變量b和一個(gè)函數(shù)sum,在文件f2.c中想引用它們,則在f2.c中必須作如下說明:externintb;externintsum(int,int);
【例5-16】輸入二元一次方程組中自變量的系數(shù)和常數(shù)項(xiàng),解此方程組:編程思路:根據(jù)數(shù)學(xué)知識,可利用系數(shù)行列式求解。令則我們把g、x、y的求解用一個(gè)函數(shù)solve解決,并單獨(dú)放在一個(gè)文件fs.c中,主函數(shù)放在文件fm.c中,它調(diào)用solve函數(shù)并向其提供數(shù)據(jù)。
文件fm.cexternvoidsolve();inta,b,c,d,e,f;floatg,x,y;#include<stdio.h>main(){printf(″Inputdata:a,b,c,d,e,f:\n″);scanf(″%d%d%d%d%d%d″,&a,&b,&c,&d,&e,&f);solve();if(g==0) printf(″Therearemanysolution!\n″);
else printf(″x=%f,y=%f\n″,x,y);
return0;}
文件fs.cexterninta,b,c,d,e,f;externfloatg,x,y;voidsolve(){g=a*e-b*d;;if(g!=0){x=(c*e-b*f)/g;y=(a*f-c*d)/g;}}運(yùn)行輸出:Inputdata:a,b,c,d,e,f:123456↙
x=-1.000000y=2.000000在文件fm.c中要對在fs.c文件中定義的函數(shù)solve作extern說明,同樣在文件fs.c中要對在fm.c文件中定義的外部變量a、b、c、d、e、f、g、x、y作extern說明。如何運(yùn)行由多個(gè)文件組成的程序呢?有兩種方法:組成擴(kuò)展名為.prj的項(xiàng)目文件;利用文件包含。
(1)建立項(xiàng)目文件的操作步驟如下(以TurboC編譯器為例):①打開“Project/Openproject”對話框。②輸入要建立的項(xiàng)目文件名,比如ms.prj。③利用“Project/Additem”命令打開文件清單。④在*.c文件中選擇fm.c和fs.c文件并加入到ms.prj文件中。⑤按ESC或Done按鈕,則建立項(xiàng)目文件完成。⑥對項(xiàng)目文件像對其他.c文件一樣進(jìn)行編譯、連接和運(yùn)行。⑦在執(zhí)行結(jié)束后,應(yīng)執(zhí)行“Project/CloseProject”操作,否則會影響后面其他文件的編譯。
(2)文件包含方法是指,在一個(gè)文件中用編譯預(yù)處理命令#include將本文件中所需的其他文件包含進(jìn)來為我所用。比如在fm.c文件中加入 #include″fs.c″這條預(yù)處理命令后,就可以直接對fs.c文件進(jìn)行編譯、連接、運(yùn)行,這時(shí)fs.c文件已是fm.c的一部分,所以在fm.c中就不需要再對fs.c文件中的solve函數(shù)作extern說明了。同樣在fs.c中也不需要對fm.c文件中定義的外部變量作extern說明了。
(3)VC++環(huán)境下建立項(xiàng)目文件的步驟。在VC中,任何C文件都被組織到項(xiàng)目(或工程,即project)中,而項(xiàng)目又是在工作區(qū)(workspace)中運(yùn)行的。一個(gè)工作區(qū)中可以有多個(gè)項(xiàng)目,一個(gè)項(xiàng)目又可以有多個(gè)C文件,其結(jié)構(gòu)形式為:工作區(qū)和項(xiàng)目可以由用戶建立,也可以自動生成。由用戶建立時(shí),其順序是先建立工作區(qū),再建立項(xiàng)目,最后再建立文件。前面只介紹了建立文件的過程,沒有提到建立工作區(qū)和項(xiàng)目,實(shí)際上,它們已由編譯系統(tǒng)自動生成了。工作區(qū)名和項(xiàng)目名均默認(rèn)地與文件名同名,但那是一個(gè)程序只有一個(gè)文件的情況。如果一個(gè)程序由多個(gè)文件組成,則必須顯式地建立項(xiàng)目文件,并把組成該程序的所有文件都組織到項(xiàng)目中。其步驟如下:
(1)打開“File/New”菜單,在New(新建)對話框中選擇Project(工程)標(biāo)簽,在左邊的列表框中選擇“Win32ConsoleApplication”選項(xiàng),即選擇控制臺工作方式。然后在右邊的“Projectname”一欄輸入項(xiàng)目名,它下面的“Location”欄用來指定項(xiàng)目所在的目錄,如圖5-10所示。圖5-10New對話框
(2)在輸入項(xiàng)目名(此處為fms)并指定其目錄后,按“OK”按鈕,并在后面出現(xiàn)的對話框中連續(xù)按回車鍵,則會出現(xiàn)如圖5-11所示的窗口。其左部窗口中顯示出“Workspace′fms′:1project[s]”,下一行是“fmsfiles”,這說明項(xiàng)目fms已被加到工作區(qū)fms中(這里工作區(qū)默認(rèn)地采用了項(xiàng)目的名字)。項(xiàng)目中包含三個(gè)文件夾:SourceFiles(源程序文件)、HeaderFiles(頭文件)和ResourceFiles(資源文件)。項(xiàng)目是所有用于創(chuàng)建一個(gè)可運(yùn)行程序的文件的集合。項(xiàng)目文件的擴(kuò)展名是.dsp,不可以用type命令顯示其內(nèi)容。圖5-11項(xiàng)目窗口
(3)執(zhí)行菜單“Project/AddToProject/Files...”,如圖5-12所示。這是為了要向項(xiàng)目文件中添加.c程序。在彈出的對話框“InsertFilesintoProject”中,選擇要加入到項(xiàng)目中的.c文件,如圖5-13所示。圖5-12添加.c程序圖5-13選擇.c程序
(4)單擊“OK”按鈕,此時(shí)如打開左邊項(xiàng)目fms的SourceFiles文件夾,可以發(fā)現(xiàn)已有兩個(gè)源程序文件fm.c和fs.c加入到了項(xiàng)目文件中。對項(xiàng)目文件不需要編譯,直接執(zhí)行“Build/Buildfms.exe”(或按F7),可生成可執(zhí)行文件.exe。該命令自動進(jìn)行編譯和連接操作,并將編譯、連接的結(jié)果顯示在屏幕下方的調(diào)試信息窗口中,如圖5-14所示。圖5-14顯示調(diào)試信息
(5)執(zhí)行菜單“Build/!Executefms.exe”(或按Ctrl+F5),運(yùn)行該可執(zhí)行文件,結(jié)果如圖5-15所示。圖5-15運(yùn)行結(jié)果5.6遞歸迄今為止,我們所用的函數(shù)調(diào)用都是以層次的方式用一個(gè)函數(shù)調(diào)用另一個(gè)函數(shù),但某些問題可以用調(diào)用函數(shù)自身的方法來解決。遞歸函數(shù)就是直接或間接調(diào)用自身的函數(shù)。5.6.1遞歸函數(shù)用遞歸解決問題,就是把問題分為兩部分:一部分屬于基本問題,對它可以直接求出結(jié)果;另一部分雖然不是基本問題,但是與原問題類似并且比原問題簡單一些。對稍微簡單一些的問題繼續(xù)進(jìn)行分解,直至達(dá)到邊界條件。解決完邊界處的問題(即基本問題)后,函數(shù)會沿著調(diào)用順序不斷給上一次的調(diào)用返回結(jié)果,直至原始問題。這看起來似乎很復(fù)雜,但事實(shí)上是個(gè)很自然的過程。遞歸函數(shù)用來解決遞歸問題。所謂遞歸問題,就是能逐漸簡化到邊界條件的問題。來看下面的例子。
【例5-17】求階乘的運(yùn)算:
n!=1(n=0或n=1) n!=n(n-1)!(n≥2)當(dāng)n大于等于2的時(shí)候,欲求n的階乘,只要先求出n-1的階乘,把所得結(jié)果乘以n即為n的階乘。至于n-1的階乘是多少,在這一步可先不考慮,設(shè)它為p。則
n!=n·p這是一步就可求出的問題,但這時(shí)p還不是一個(gè)確定的數(shù),還要等待返回結(jié)果。接下來求p=(n-1)!,再把它進(jìn)行分解。它等于n-1去乘n-2的階乘,即p=(n-1)·(n-2)!,設(shè)(n-2)!=q,則p=(n-1)·q。接下來的問題是再去求q。按照上述方法繼續(xù)類似的操作,問題一步步地化簡,最終一定能達(dá)到求1的階乘的地步,這時(shí)就稱達(dá)到了邊界條件,可以直接求解了。任何遞歸調(diào)用都有一個(gè)邊界條件,否則遞歸調(diào)用將會無限地進(jìn)行下去。在上面的計(jì)算中,每一步都要等待下一步的計(jì)算結(jié)果,因此它當(dāng)時(shí)的狀態(tài)就得先保存起來,等下步的結(jié)果返回后再進(jìn)行基本運(yùn)算。當(dāng)?shù)竭_(dá)邊界條件后,可求出確定的值,于是返回到上一步,而上一步當(dāng)時(shí)的狀態(tài)已被保存,只等有返回值后就可進(jìn)行直接運(yùn)算,它運(yùn)算的結(jié)果又繼續(xù)向上一步返回,這樣一步步向上返回,直至原始問題得解。從上面的分析可以看出,求解遞歸問題有兩個(gè)過程:第一個(gè)過程是遞歸調(diào)用,從原始問題出發(fā),層層向下,直至邊界條件;第二個(gè)過程是返回過程,從邊界條件出發(fā),層層向上返回,直至原始問題。通過遞歸調(diào)用圖,就可以清楚地看到這兩個(gè)過程。
【例5-18】求4!的遞歸調(diào)用,調(diào)用圖如圖5-16所示。
4!
4!=24 ↓ ↑返回4!=24(等待返回3!)4×3! 4×6 ↓ ↑返回3!=6(等待返回2!)3×2! 3×2 ↓ ↑返回2!=2 2×1!邊界條件2×1(遞歸調(diào)用過程) (返回過程)圖5-16求4!的遞歸調(diào)用圖下面是求n的階乘的程序:#include<stdio.h>longintfac(int);main(){inti;for(i=1;i<=10;i++)printf(″%2d!=%ld″,i,fac(i));return0;}longintfac(intn){if(n<=1)return1;elsereturnn*fac(n-1);}運(yùn)行輸出:1!=12!=23!=64!=245!=1206!=7207!=50408!=403209!=36288010!=3628800
說明:①從輸出結(jié)果看,階乘增加得非???,7以上的階乘就超過了int類型的取值范圍,這就是為什么要把返回值的類型定義為longint的原因,相應(yīng)的輸出格式是′%ld′。如果要求更大數(shù)的階乘,可以選用float或double作為階乘函數(shù)返回值類型。②求階乘函數(shù)中有個(gè)核心語句:雙分支的if語句。其中的一個(gè)分支描述的是邊界條件,另一個(gè)分支是遞歸調(diào)用。在遞歸調(diào)用中又出現(xiàn)了該函數(shù)的函數(shù)名,但這里的參數(shù)比函數(shù)頭中的參數(shù)就簡單了一些,這就是遞歸函數(shù)的書寫模式。對于大多數(shù)遞歸函數(shù),都可以這樣考慮和書寫。在遞歸函數(shù)體中還可以出現(xiàn)不只一個(gè)的遞歸調(diào)用,它們各自有不同的參數(shù)。
【例5-19】求Fibonacci數(shù)列:1,1,2,3,5,8,13,…觀察數(shù)列,可發(fā)現(xiàn)這樣的規(guī)律:從第3項(xiàng)開始,每一項(xiàng)都是其前面兩項(xiàng)之和。設(shè)fib(n)[JP]表示第n個(gè)Fibonacci數(shù),則有:
fib(n)=1(n=1或n=2) fib(n)=fib(n-1)+fib(n-2)(n≥3)
Fibonacci數(shù)列是個(gè)很有趣的數(shù)列,隨著項(xiàng)數(shù)的增加,前后相鄰兩項(xiàng)的比值趨向于0.618,這個(gè)比值被稱為“黃金分割”。建筑師經(jīng)常用黃金分割來指導(dǎo)設(shè)計(jì)門窗、房間和建筑物等,人們認(rèn)為符合黃金分割的物體是一種美的物體。
Fibonacci函數(shù)的數(shù)學(xué)表達(dá)式已清楚地表明這是個(gè)遞歸問題,因此可以很容易地用C語言寫出它的遞歸函數(shù)。#include<stdio.h>longfib(int);main(){longbf;intn;printf(″Enteraninteger:\n″);scanf(″%d″,&n);bf=fib(n);printf(″Fibonacci(%d)=%ld″,n,bf);return0;}longfib(intm){if(m==1‖m==2)return1;elsereturnfib(m-1)+fib(m-2);}運(yùn)行輸出:Enteraninteger:8↙
Fibonacci(8)=21
fib函數(shù)的遞歸調(diào)用如圖5-17所示(這里是求fib(5))。圖5-17求fib(5)的遞歸調(diào)用
對符合邊界條件的基本問題可直接求出其解。這里涉及到對操作數(shù)求值的次序問題。在fib(3)=fib(2)+fib(1)中,是先計(jì)算fib(2)還是先計(jì)算fib(1),ANSIC標(biāo)準(zhǔn)對此沒有作出明確的規(guī)定,因此程序可對調(diào)用順序不作任何假定。對本程序和大多數(shù)程序而言,計(jì)算結(jié)果都是正確的,但在某些程序中,操作數(shù)的運(yùn)算順序可能會影響表達(dá)式的最終結(jié)果。在C語言的所有運(yùn)算符中,ANSIC只規(guī)定了下面四種運(yùn)算符對操作數(shù)的求值順序:&&、‖、逗號和?:。前三個(gè)運(yùn)算符是雙目運(yùn)算符,按從左到右的順序計(jì)算它的兩個(gè)操作數(shù)。最后一個(gè)是C語言中惟一的一個(gè)三目運(yùn)算符,它最左邊的操作數(shù)優(yōu)先運(yùn)算。至于其他運(yùn)算符操作數(shù)的求值順序,不同的系統(tǒng)有不同的規(guī)定。因此如果編寫的程序依賴于操作數(shù)的求值順序,將可能導(dǎo)致錯誤,因?yàn)榫幾g器的執(zhí)行順序不一定會和編程者的想法一致。另外,遞歸函數(shù)的調(diào)用次數(shù)也是個(gè)值得注意的問題。以本題為例,每次遞歸調(diào)用fib函數(shù)都要再遞歸地調(diào)用兩次,因此當(dāng)n很大時(shí),對fib函數(shù)的調(diào)用次數(shù)將是驚人的,所以應(yīng)該避免編寫像fib函數(shù)這樣的遞歸函數(shù),即使要編寫,參數(shù)也不應(yīng)很大。
【例5-20】把一個(gè)正整數(shù)的各位數(shù)字以字符形式輸出。#include<stdio.h>voiddigit(int);main(){intm;printf(″Inputaninteger:\n″);scanf(″%d″,&m);digit(m);return0;}voiddigit(intn){if(n==0)return;else{digit(n/10);putchar(n%10+′0′);}}
digit()是個(gè)遞歸函數(shù),其參數(shù)為n,而在該函數(shù)中又以n/10為參數(shù)進(jìn)行遞歸調(diào)用,則調(diào)用是向參數(shù)減少的方向變化的。當(dāng)n變?yōu)?時(shí),達(dá)到邊界條件,遞歸調(diào)用結(jié)束,開始回退。在回退的過程
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 個(gè)人房產(chǎn)買賣標(biāo)準(zhǔn)協(xié)議樣本(2024年版)版B版
- 個(gè)人債權(quán)轉(zhuǎn)讓協(xié)議(2024版)3篇
- 個(gè)人手車買賣合同
- 專業(yè)軟件技術(shù)開發(fā)服務(wù)協(xié)議(2024年更新版)版B版
- 二零二四商場LED顯示屏采購與安裝合同
- 2025年度城市綜合體配套廠房建造與裝修承包合同范本4篇
- 2025年度廠房土地開發(fā)及使用權(quán)出讓合同4篇
- 2025年度插座產(chǎn)品售后服務(wù)網(wǎng)絡(luò)建設(shè)合同4篇
- 2025年度科技園區(qū)場地轉(zhuǎn)租及知識產(chǎn)權(quán)保護(hù)協(xié)議4篇
- 2024年05月上海華夏銀行上海分行招考筆試歷年參考題庫附帶答案詳解
- 春節(jié)行車安全常識普及
- 電機(jī)維護(hù)保養(yǎng)專題培訓(xùn)課件
- 汽車租賃行業(yè)利潤分析
- 春節(jié)拜年的由來習(xí)俗來歷故事
- 2021火災(zāi)高危單位消防安全評估導(dǎo)則
- 佛山市服務(wù)業(yè)發(fā)展五年規(guī)劃(2021-2025年)
- 房屋拆除工程監(jiān)理規(guī)劃
- 醫(yī)院保安服務(wù)方案(技術(shù)方案)
- 高效能人士的七個(gè)習(xí)慣:實(shí)踐應(yīng)用課程:高級版
- 小數(shù)加減法計(jì)算題100道
- 通信電子線路(哈爾濱工程大學(xué))智慧樹知到課后章節(jié)答案2023年下哈爾濱工程大學(xué)
評論
0/150
提交評論