C語言程序設計05(模塊化程序設計)_第1頁
C語言程序設計05(模塊化程序設計)_第2頁
C語言程序設計05(模塊化程序設計)_第3頁
C語言程序設計05(模塊化程序設計)_第4頁
C語言程序設計05(模塊化程序設計)_第5頁
已閱讀5頁,還剩121頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

C語言程序設計第5章模塊化程序設計第5章內容模塊化程序設計思想函數的定義函數的調用函數的聲明函數的嵌套調用函數的遞歸調用變量的作用域變量的存儲類型內部函數和外部函數模塊化程序設計思想在軟件工程中,通常采用“自頂向下、分而治之”的方法,將大問題分解成若干小的問題,如果小問題還不容易解決,就再分解成更小的問題。模塊化程序設計的基本思想,是將一個大的復雜的程序按功能分割成一些小的功能模塊。當開發一個軟件系統時,最好的辦法是從編寫主程序開始,在主程序中,將問題作為一個整體考慮,找出完成任務的主要步驟,再沿著這條主線將整個問題繼續分解為獨立的簡單模塊,這就是模塊化程序設計的主要思想。模塊分解的原則模塊分解——“自頂向下、逐步求精”的程序設計過程模塊分解的基本原則——高聚合、低耦合及信息隱藏高聚合——一個模塊只能完成單一的功能,不能“身兼數職”低耦合——模塊之間參數傳遞盡量少,也不能通過全局變量來實現數據傳遞信息隱藏——把不需要調用者知道的信息都包裝在模塊內部隱藏起來凡是被眾多模塊公用的程序,均應設計成一個獨立的模塊各模塊間應在功能上、邏輯上相互獨立,模塊間的接口應盡量簡單,其數據傳遞使用參數來完成每個模塊應設計成單入口、單出口形式,以便調試與閱讀,提高程序的可靠性模塊化設計方法功能分解自頂向下、逐步求精的過程。每個模塊的功能盡量單一,程序代碼最好不要超過50行。模塊分解的原則保證模塊的相對獨立性(高聚合、低耦合)。模塊的實現細節對外不可見(外部:關心做什么;內部:關心怎么做)。設計好模塊接口接口是指羅列出一個模塊的所有的與外部打交道的變量等。定義好后不要輕易改動。在模塊開頭(文件的開頭)進行函數聲明。模塊化程序設計的特點各模塊相對獨立、功能單一、結構清晰、接口簡單程序設計的復雜性得到了有效控制縮短開發周期避免程序開發的重復勞動易于維護和功能擴充模塊與函數在C語言中,每個模塊都是由函數完成的,一個小模塊就是一個函數。將一些常用的功能模塊編寫成函數,放在函數庫中,組成C庫函數,供公共選用。編寫某個函數時,遇到具有相對獨立功能的程序段,都應獨立成另一個函數,供另一個函數調用;當某一個函數代碼較長時,也應將其中相對獨立的代碼分成另一個函數從函數定義的角度看,C語言的函數可分為庫函數、用戶定義函數(包括主函數main)兩種。從函數的使用性質來看,C語言的函數還分為內部函數、外部函數兩種。函數的本質是外部函數。函數設計的原則函數的功能要單一,不要設計多用途的函數函數的規模要小,盡量控制在50行代碼以內1986年IBM在OS/360的研究結果:大多數有錯誤的函數都大于500行1991年對148,000行代碼的研究表明:小于143行的函數比更長的函數更容易維護參數和返回值的規則參數要書寫完整,不要省略對函數的入口參數進行有效性檢查沒有參數和返回值時,用void填充每個函數只有一個入口和一個出口,盡量不使用全局變量盡量少用靜態局部變量,以避免使函數具有“記憶”功能函數的定義合法標識符函數返回值類型:缺省int型,無返回值void函數體函數類型函數名(形式參數類型說明表){

聲明部分 執行部分}現代風格:例有參函數(現代風格)

intmax(intx,inty){intz;z=x>y?x:y;return(z);}例空函數

dummy(){}函數體為空例無參函數voidprintstar(){printf(“**********\n”);}或voidprintstar(void){printf(“**********\n”);}參數:傳遞數據信息的通道函數定義說明①類型說明符可為:int、char、float、double等。表示在調用了該函數后,其返回值的數據類型;如果函數無數據返回時,應使用void

作類型定義符。注意,省略了類型說明符,編譯程序默認的函數返回值為一個整型值類型(int)。②函數名,由用戶取的合法標識符。C語言的關鍵字不能作函數名。自定義函數的名稱可以使用庫函數名,但這時庫函數被屏蔽。③形式參數表是一個用逗號分隔的變量表,當函數被調用時這些變量接受調用參數的值。相當于函數調用時傳遞信息的通道。④在函數的定義中,如果沒有函數體,即函數什么功能都不做,稱為空函數。空函數的功能主要是在程序設計中,留出該函數的功能,以后在需要的時候補充上去。特別注意(1)函數不能單獨運行,函數可以被主函數或其它函數調用,也可以調用其它函數,但是不能調用主函數。(2)C規定,函數體的聲明部分和執行部分應嚴格劃分,且聲明部分放在函數體的開始。例如,以下定義是不允許的:voidmain(){doublex;scanf("%lf",&x);

doubles;/*不能在執行語句中穿插定義變量*/scanf("%lf",&s);…}主調函數與被調函數C語言程序是由若干個函數組成的,各函數在結構上是獨立的,但它們所處理的對象即數據卻是相互聯系的。一個函數調用另一個函數,將調用的函數稱為主調函數,將被調用的函數稱為被調函數。主調函數和被調函數具有數據傳遞的關系,其傳遞的實施是通過函數的參數來實現的。主調函數和被調函數的概念是相對的。函數調用的一般形式#include<stdio.h>voidmain(){inta=5,b=7,c;c=max(a,b);printf(“較大的數是%d",c);}

intmax(intx,inty){intz;z=x>y?x:y;return(z);}作為一個表達式調用,調用后只是返回一個結果值函數調用過程中的參數傳遞c=max(a,b);

intmax(intx,inty){intz;z=x>y?x:y;return(z);}按順序對應參數傳遞實參數形式參數函數調用過程中的結果返回c=;

intmax(intx,inty){intz;z=x>y?x:y;return(z);}max(a,b)57=77777無返回函數的調用#include<stdio.h>voidmain(){printstar();}voidprintstar(){printf(“**********\n”);}作為一個函數語句調用函數調用說明如果調用的是無參函數,“實際參數表”可以沒有,但括號()不能省略。如果實參表包含多個實參,則各參數間用逗號隔開。實參與形參的個數應相等、類型應一致。實參與形參按順序對應,一一傳遞數據。函數不能嵌套定義,即在一個定義好的函數中,不能又定義另一個函數。函數之間允許相互調用,也允許嵌套調用。但main函數不能被調用。函數還可以自己調用自己,稱為遞歸調用。補充實例1求任意正整數n的階乘n!=1×2×3×……×n計算階乘#include<stdio.h>voidmain(){ longi,m,n; printf("請輸入一個正整數:");

scanf("%d",&n); //鍵盤輸入一個正整數n

m=1; for(i=2;i<=n;i++){ m*=i; } printf("%d!=%d\n",n,m);}編寫一個計算階乘的函數longfact(intn){ longi,m;

m=1; for(i=2;i<=n;i++){ m*=i; } return(m);}補充實例2計算以下公式的值(其中n為鍵盤輸入的任意數)sum=1!+2!+3!+……+n!計算階乘之和#include<stdio.h>voidmain(){ longi,sum=0,n; printf("請輸入一個正整數:");

scanf("%d",&n);

for(i=1;i<=n;i++){ sum+=fact(i); } printf("計算結果=%d\n",sum);}longfact(intn){longi,m;

m=1;for(i=2;i<=n;i++){ m*=i;}return(m);}補充實例3計算以下公式的值(其中n為鍵盤輸入的任意數)longfact(intn){longi,m;

m=1;for(i=2;i<=n;i++){ m*=i;}return(m);}計算多項式之和的程序#include<stdio.h>voidmain(){ longi,n; doublesum=1.0; printf("請輸入一個正整數:");

scanf("%d",&n); for(i=1;i<=n;i++){ sum+=1.0/fact(i); } printf("計算結果=%f\n",sum);}判斷一個數是否為素數的程序#include<stdio.h>#include<math.h>voidmain(){inti,m,flag;printf("請輸入一個正整數m>2:");

scanf("%d",&m); //鍵盤輸入一個正整數m

flag=1;

//假設m是素數

for(i=2;i<=sqrt(m)+1;i++){if(m%i==0) //若能被i整除,返回1,說明m不是素數{ flag=0; break;} //跳出循環,減少無效的循環次數}

if(flag==1)printf("%d是素數\n",m);elseprintf("%d不是素數\n",m);}補充實例4找出50~100之間的所有素數,并在屏幕上輸出找出50~100之間的所有素數#include"stdio.h"#include"math.h"voidmain(){ inti,j,flag; for(i=50;i<=100;i++) { flag=1; for(j=2;j<=sqrt(i)+1;j++) { if(i%j==0) { flag=0; break; } } if(flag==1)printf("%d",i); }}自定義一個專門判斷是否素數的函數intPrime(intnumber){inti,flag=1;

for(i=2;i<=sqrt(number)+1;i++){ if(number%i==0) { flag=0; break;} }

return(flag);

}找出50~100之間的所有素數#include<stdio.h>#include<math.h>voidmain(){ inti;

for(i=50;i<=100;i++){ if(Prime(i))

{ printf("%5d",i); } }}intPrime(intnumber){inti,flag=1;for(i=2;i<=sqrt(number)+1;i++){if(number%i==0) { flag=0; break;} }

return(flag);

}500找出50~100之間的所有素數#include<stdio.h>#include<math.h>voidmain(){ inti;

for(i=50;i<=100;i++){ if(Prime(i))

{ printf("%5d",i); } }}intPrime(intnumber){inti,flag=1;for(i=2;i<=sqrt(number)+1;i++){if(number%i==0) { flag=0; break;} }

return(flag);

}510找出50~100之間的所有素數#include<stdio.h>#include<math.h>voidmain(){ inti;

for(i=50;i<=100;i++){ if(Prime(i))

{ printf("%5d",i); } }}intPrime(intnumber){inti,flag=1;for(i=2;i<=sqrt(number)+1;i++){if(number%i==0) { flag=0; break;} }

return(flag);

}520找出50~100之間的所有素數#include<stdio.h>#include<math.h>voidmain(){ inti;

for(i=50;i<=100;i++){ if(Prime(i))

{ printf("%5d",i); } }}intPrime(intnumber){inti,flag=1;for(i=2;i<=sqrt(number)+1;i++){if(number%i==0) { flag=0; break;} }

return(flag);

}531自定義頭文件student.h#include<stdio.h>#include<math.h>#include"e:\student.h"voidmain(){ inti;

for(i=50;i<=100;i++){ if(Prime(i))

{ printf("%5d",i); } }}把自定義函數保存在自行編寫的頭文件中,是一個方便實用的方法。用戶自行編寫的頭文件,最好不要與系統提供的頭文件保存在同一路徑下。補充實例5查找1000以內的最大素數查找1000以內最大的素數#include"stdio.h"#include"math.h"voidmain(){ inti,j,flag; for(i=1000;i>=2;i--) //注意循環控制方法的改變 { flag=1; for(j=2;j<=sqrt(i)+1;j++) { if(i%j==0) { flag=0; break; } } if(flag==1) { printf("1000以內的最大素數是:%d\n",i); break; } }}查找1000以內的最大素數#include<stdio.h>#include<math.h>voidmain(){ inti,m; for(i=1000;i>=2;i--){ if(Prime(i)) { printf("1000以內的最大素數為%d\n",i); break; } }}補充實例6哥德巴赫猜想:一個足夠大的偶數,必定可以表示成兩個素數之和分析思路:設計一個判斷任意指定數是否為素數的子函數把輸入的偶數拆分成兩個數,并分別調用上述子函數,判斷它們是否為素數例如:128=19+109驗證哥德巴赫猜想的程序代碼#include<stdio.h>#include<math.h>voidmain(){inti,j,m,flag1,flag2;printf("請輸入一個足夠大的偶數:");scanf("%d",&m);for(i=2;i<=m/2;i++) {

flag1=1; for(j=2;j<=sqrt(i)+1;j++) { if(i%j==0) { flag1=0; break; } }

flag2=1; for(j=2;j<=sqrt(m-i)+1;j++) { if((m-i)%j==0) { flag2=0; break; } } if(flag1==1&&flag2==1) { printf("%d=%d+%d\n",m,i,m-i); break; } }}驗證哥德巴赫猜想的程序代碼#include<stdio.h>#include<math.h>voidmain(){ inti,m;printf("請輸入一個足夠大的偶數:");

scanf("%d",&m); //鍵盤輸入一個正整數m

for(i=2;i<=m/2;i++){ if(Prime(i)&&Prime(m-i)) { printf("%d=%d+%d\n",m,i,m-i); break; } }}課堂作業1寫出可以根據主調函數提供的數值n,用來計算以下多項式之和的函數sum=1+2+3+……+n課堂作業2編寫一個能根據提供的數值n,在屏幕上輸出一行n個星號的函數(n由主調函數傳遞而來)課堂作業3編寫一個能根據提供的數值n,在屏幕上輸出由星號組成的如下圖案的函數(本例設n=4)****************課堂作業4編寫一個能根據提供的數值n,在屏幕上輸出由星號組成的如下圖案的函數(本例設n=5,并限制n小于10)*************************課堂作業5判斷鍵盤輸入的一個正整數是不是回文數所謂回文數,就是從左看或從右看均是同一個數的數,如2552、73837判斷是否回文數的程序#include<stdio.h>voidmain(){longm=0,n,p;printf("請輸入一個正整數:");scanf("%d",&n); //鍵盤輸入一個正整數n p=n; while(p>0) { m=m*10+p%10; p=p/10; } if(n==m) printf("%d是回文數\n",n); else printf("%d不是回文數\n",n);}回文數計算過程x=32768y=0y=y*10+x%10=0*10+8=8x=x/10=3276y=y*10+x%10=8*10+6=86x=x/10=327回文數計算過程y=y*10+x%10=86*10+7=867x=x/10=32y=y*10+x%10=867*10+2=8672x=x/10=3y=y*10+x%10=8672*10+3=86723x=x/10=0課堂作業5寫出判斷給定的數n是否為回文數的函數,若n為回文數,函數返回值為1,否則返回值為0課堂作業1答案#include<stdio.h>intsum(intx){ inti,s=0; for(i=1;i<=x;i++) s+=i; return(s);}voidmain(){intn;printf("請輸入一個正整數:");scanf("%d",&n); //鍵盤輸入一個正整數n printf("累加和=%d\n",sum(n));}課堂作業2答案#include<stdio.h>voidstars(intx){ inti; for(i=1;i<=x;i++) printf("*"); printf("\n");}voidmain(){intn; printf("請輸入一個正整數:");scanf("%d",&n); //鍵盤輸入一個正整數n stars(n); //注意調用方式}課堂作業3答案#include<stdio.h>voidstars(intx){ inti,j; for(i=1;i<=x;i++) { for(j=1;j<=2*i-1;j++) printf("*"); printf("\n"); }}voidmain(){intn; printf("請輸入一個正整數:");scanf("%d",&n); //鍵盤輸入一個正整數n stars(n);}課堂作業4答案#include<stdio.h>voidstars(intx){ inti,j,k; for(i=1;i<=x;i++) { for(k=1;k<20-2*i+1;k++) printf(""); for(j=1;j<=2*i-1;j++) printf("*"); printf("\n"); }}voidmain(){intn; printf("請輸入一個正整數:");scanf("%d",&n); //鍵盤輸入一個正整數n stars(n);}課堂作業5答案charhuiwen(longn){ longm=0,p; p=n; while(p>0) { m=m*10+p%10; p=p/10; } if(m==n) return(1); else return(0);}找出1~999之間的所有回文數#include<stdio.h>voidmain(){longi;for(i=1;i<=999;i++) { if(huiwen(i)) printf("%5d",i); }}找出n和n2均為回文數的數#include<stdio.h>voidmain(){longi;for(i=1;i<=999;i++) { if(huiwen(i)&&huiwen(i*i)) printf("%10d%10d\n",i,i*i); }}例5.1【例5.1】求三個數中最大數和最小數的差值。分析問題:首先在主函數main()中調用求差值的函數dif()在求差值函數中再調用求三個數的最大值函數max()和最小值函數min()將一個復雜的問題分解成了三個小問題:求兩個數的差求三個數的最大值求三個數的最小值程序實現#include<stdio.h>/*自定義函數的聲明*/intdif(intx,inty,intz);intmax(intx,inty,intz);intmin(intx,inty,intz);intmain(){inta,b,c,d;printf("InputData:");scanf("%d%d%d",&a,&b,&c);/*輸入三個數*/

/*調用差值函數,并將函數的返回值賦給d*/d=dif(a,b,c);printf("Max-Min=%d\n",d);/*輸出*/return(0);}程序實現(續)/*定義求三個數x、y、z的差值函數dif*/intdif(intx,inty,intz){intm1,m2;m1=max(x,y,z); /*調用求最大值函數,返回值賦給m1*/m2=min(x,y,z); /*調用求最小值函數,返回值賦給m2*/returnm1-m2; /*返回最大值與最小值的差*/}/*定義求三個數x、y、z的最大值函數max*/intmax(intx,inty,intz){intr1,r2;r1=(x>y)?x:y;r2=(r1>z)?r1:z;return(r2); /*返回三個數的最大值*/}/*定義求三個數x、y、z的最小值函數min*/intmin(intx,inty,intz){intr;r=(x<y)?x:y;return(r<z?r:z); /*返回三個數的最小值*/}程序的調用關系main()調用函數dif輸出結束dif函數max函數調用函數max調用函數minmin函數本例程序涉及到模塊化程序設計思想、函數的定義、函數的調用、函數的聲明、變量的作用域、變量的存儲類型、內部函數和外部函數等知識點。例5.2【例5.2】用函數實現:編程求的值。分析問題:可以定義一個函數doublefff(doublex)來求值,然后在主函數main()中調用該函數。例5.2程序#include<stdio.h>doublefff(doublex){doublef;f=x*x;return(f);}/*主函數中調用fff()函數*/intmain(){doublef=0.3,ff;ff=fff(f);printf(“%f\n”,ff);return(0);}函數參數傳遞形參與實參形式參數:定義函數時函數名后面括號中的變量名實際參數:調用函數時函數名后面括號中的表達式c=max(a,b);(main函數)(max函數)max(intx,inty){intz;z=x>y?x:y;return(z);}例5.3

編程求兩個數的最大值#include<stdio.h>intmax(intx,inty){intz;z=x>y?x:y;return(z);}intmain(){inta,b,c;scanf("%d,%d",&a,&b);c=max(a,b);printf("Maxis%d",c);return(0);}形參實參(主調函數)c=max(a,b);(被調函數)max(intx,inty)實際參數形式參數參數傳遞參數傳遞——值傳遞方式值傳遞方式方式:函數調用時,為形參分配單元,并將實參的值復制到形參中;調用結束,形參單元被釋放,實參單元仍保留并維持原值特點:形參與實參占用不同的內存單元單向傳遞實參a10形參x10復制實參內存空間形參內存空間參數傳遞——地址傳遞方式地址傳遞方式:函數調用時,將數據的存儲地址作為參數傳遞給形參特點:形參與實參占用同樣的存儲單元“雙向”傳遞實參和形參必須是地址常量或變量實參a形參x實參內存空間形參內存空間函數的返回值返回語句形式:return(表達式);或return表達式;或return;功能:使程序控制從被調用函數返回到調用函數中,同時把返值帶給調用函數說明:函數中可有多個return語句,但只能返回唯一的函數值若無return語句,遇

}

時,自動返回調用函數,返回值是一個不確定的值若函數類型與return語句中表達式值的類型不一致,按函數類型為準,表達式值類型在函數調用時自動轉換成函數類型void型函數:表示“空類型”或“無類型”例無返回值函數

voidswap(intx,inty){inttemp;temp=x;x=y;y=temp;}例有返回值函數intmaxmum(intx,inty){intz;z=(x>y)

?x:y

;return(z);/*返回最大值*/}函數返回不確定值

intprintstar(){printf("**********");}intmain(){inta;a=printstar();printf("%d",a);return(0);}輸出:10voidprintstar(){printf("**********");}intmain(){inta;a=printstar();printf("%d",a);return(0);}編譯錯誤!函數返回值類型轉換#include<stdio.h>intmain(){floata,b;intc;scanf("%f,%f",&a,&b);c=max(a,b);printf("Maxis%d\n",c);return(0);}intmax(floatx,floaty){floatz;z=x>y?x:y;return(z);}函數值轉換成float函數返回值類型int例5.4【例5.4】用函數實現:編程計算兩個數之和。分析問題:可以定義一個函數intadd(intx,inty)來求兩個數之和的值,并通過return語句將和值返回到主函數中。例5.4程序#include<stdio.h>intadd(intx,inty){intz;z=a+b;return(z);}intmain(){inta,b,c;scanf("%d%d",&a,&b);c=add(a,b);printf("sum=%d\n",c);return(0);}函數聲明對被調用函數要求:必須是已存在的函數庫函數:#include<*.h>用戶自定義函數:函數類型聲明函數聲明一般形式:函數類型函數名(形參類型[形參名],…..);或函數類型函數名();作用:告訴編譯系統函數類型、參數個數及類型,以便檢驗函數定義與函數聲明不同函數聲明位置:程序的數據聲明部分(函數內或外)下列情況下,可不作函數聲明若函數返值是char或int型,系統自動按int型處理被調用函數定義出現在主調函數之前有些系統(如BorlandC++)要求函數聲明指出函數返值類型和形參類型,并且對void和int

型函數也要進行函數聲明函數聲明舉例voidmain(){floata,b;intc;scanf("%f,%f",&a,&b);

c=max(a,b);printf("Maxis%d\n",c);}intmax(floatx,floaty){floatz;z=x>y?x:y;return(z);}int型函數可不作函數說明(BorlandC++不行)floatadd(floatx,floaty){floatz;z=x+y;return(z);}voidmain(){floata,b,c;scanf("%f,%f",&a,&b);

c=add(a,b);printf("sumis%f",c);}被調函數出現在主調函數之前,不必函數說明voidmain(){

floatadd(float,float);/*functiondeclaration*/

floata,b,c;scanf("%f,%f",&a,&b);c=add(a,b);printf("sumis%f",c);}floatadd(floatx,floaty){floatz;z=x+y;return(z);}可以:floatadd();不提倡。或:floatadd(floatx,floaty);例5.5【例5.5】用函數實現:求兩個數的最大公約數和最小公倍數。分析問題:

最大公約數(或稱最大公因子)是能夠同時被兩個數整除的最大數,其性質為:

①如果a>b,則a和b的最大公約數與a-b和b的最大公約數相同;②如果b>a,則a和b的最大公約數與a和b-a的最大公約數相同;③如果a=b,則a和b的最大公約數與a值和b值相同。換句話說,最大公約數是最后能使余數為0的被除數。最小公倍數為:兩數相乘,再除以最大公約數。例5.5解法一(窮舉法)#include<stdio.h>/*函數聲明*/inthcf(intu,intv);intlcd(intu,intv,inth);intmain(){intn1,n2,h,l;scanf("%d,%d",&n1,&n2); /*輸入兩個數*/h=hcf(n1,n2); /*調用求最大公約數函數*/printf("greatestcommondivisor:%d\n",h);l=lcd(n1,n2,h); /*調用求最小公倍數函數*/printf("leasecommonmultiple:%d\n",l);return(0);}例5.5解法一(續)/*函數定義:求兩個數的最大公約數*/inthcf(intu,intv){intt;if(v>u){t=u;u=v;v=t;}

t=v;while(u%t!=0||v%t!=0)t--;return(t);}/*函數定義:求兩個數的最小公倍數,h為最大公約數*/intlcd(intu,intv,inth){return(u*v/h);}例5.5程序解法二(輾轉相除法)#include<stdio.h>/*函數聲明*/inthcf(intu,intv);intlcd(intu,intv,inth);intmain(){intn1,n2,h,l;scanf("%d,%d",&n1,&n2);h=hcf(n1,n2);printf("greatestcommondivisor:%d\n",h);l=lcd(n1,n2,h);printf("leasecommonmultiple:%d\n",l);return(0);}例5.5解法二(續)/*函數定義:求兩個數的最大公約數*/inthcf(intu,intv){intt;if(v>u){t=u;u=v;v=t;}

while(v!=0){t=u%v;u=v;v=t;}return(u);}/*函數定義:求兩個數的最小公倍數,h為最大公約數*/intlcd(intu,intv,inth){return(u*v/h);}函數的嵌套調用C程序由若干函數組成,各函數在結構上是相互平行和獨立的,函數不能嵌套定義。如果不考慮函數的功能和邏輯關系,函數之間是平行的,無主從輕重,可以相互調用。C規定:函數不能嵌套定義,但可以嵌套調用。主函數可以調用自定義函數,自定義函數之間可以嵌套調用。所謂函數嵌套調用是指一個函數調用另一個函數,另一個函數再調用其他函數的層層調用方式。函數的嵌套調用關系圖main()調用函數a結束函數a調用函數b函數b⑴⑵⑶⑷⑸⑹⑺⑻⑼調用函數d調用函數c函數c⑽⑾⑿⒁⒂函數d⒃⒀⒄例5.6【例5.6】使用函數的嵌套調用方法計算:分析問題:從表達式中可以看出,每項都是一樣的,不同的是起止數不同,因此每項的計算可以由一個相同的函數sum()來完成。而每項中還有一個階乘,因此還需要一個求階乘的函數fac()。例5.6程序#include<stdio.h>doublefac(intn);doublesum(intn1,intn2);intmain(){doubles;s=sum(1,3)+sum(6,9)+sum(12,15);printf("\ns=%f",s);return(0);}/*求項的值:n1和n2為起止數*/doublesum(intn1,intn2){inti;doubles=0;for(i=n1;i<=n2;i++)s=s+1/fac(i);return(s);}例5.6程序(續)/*求n的階乘函數*/doublefac(intn){doubles=n;if(n<=1)return1;for(;--n;)s*=n;return(s);}

main()調用函數sum結束函數fac例5.6函數嵌套調用關系用遞歸方法求n!值【例5.7】用遞歸方法求n!值。分析:使用遞歸方法。如計算5!可以用 5!=5×4! 4!=4×3! 3!=3×2! 2!=2×1! 1!=1其遞歸公式如下:n!=1當(n=0,1)//遞歸結束條件n×(n-1)!當(n>1)//遞歸方式分析問題求n!時,要先求(n-1)!;求(n-1)!時,要先求(n-2)!值;……;直到求2!要知道1!為止。這個過程稱之為“回推”過程。規定1!=1;這時就可以求2!,……;當求出(n-1)!值時,就可以求出n!。這個過程稱之為“遞推”過程。“回推”和“遞推”過程構成了“遞歸”問題的兩個階段。如果要求遞歸過程在有限步驟內,就必須有一個結束遞歸過程的條件。n!問題的結束條件就是1!=1,過程就是n!=(n-1)!*n。“回推”和“遞推”5!5×4!4×3!3×2!2×1!15!4!×53!×42!×31!×21回推過程返回1返回1!×2=2返回2!×3=6返回3!×4=24返回4!×5=120終值120遞推過程調用函數函數返回值例5.7程序#include<stdio.h>longfac(intn); /*自定義函數聲明*/intmain(){intn;longr; /*定義長整型變量存放n的階乘的值*/printf("\nInputn:");scanf("%d",&n);if(n<0) /*當n正確時求階乘,錯誤時則提示*/printf("InputDataerror(n<0).\n");else{r=fac(n); /*調用求n的階乘函數,返回r*/printf("%d!=%ld\n",n,r);/*輸出n和r的值*/}return(0);}/*定義求n的階乘的遞歸函數fac*/longfac(intn){longr;if(n==0||n==1)r=1;/*結束條件,其返回值是1*/elser=fac(n-1)*n;/*遞歸方式,返回n!值*/return(r);/*返回fac的函數值*/}函數的遞歸調用

在函數調用中,如果直接或間接地調用該函數本身,稱為遞歸調用。遞歸有時也稱為循環定義。遞歸又分為:直接遞歸調用,即函數直接調用自身。和間接調用,即函數互相調用對方。fun()調fun調g調ff()g()intfun(intx){inty,z;……

z=fun(y);…….return(2*z);}intf(intx){inty,z;……

z=g(y);…….return(2*z);}intg(intt){inta,c;……

c=f(a);…….return(3+c);}遞歸調用的說明上述兩種遞歸調用都是無終止的自身調用。程序中不應出現無終止調用,而只應出現有限次數的有終止的遞歸調用。可以使用if語句來控制。遞歸程序由遞歸方式與遞歸終止條件兩部分組成。即一個遞歸的問題可以分為:首先“回推”,然后“遞推”。在遞歸過程中,必須具有一個結束遞歸過程的條件。C編譯系統對遞歸函數的自調用次數沒有限制。每調用函數一次,在內存堆棧區分配空間,用于存放函數變量、返回值等信息,所以遞歸次數過多,可能引起堆棧溢出。遞歸形式當有函數返回值時,通用的遞歸函數體表述如下:if(遞歸結束條件)return(遞歸公式的初值);elsereturn(遞歸函數調用返回的結果值);有時遞歸函數并不返回任何值,只是輸出顯示結果。有時遞歸子問題和原問題的形式并不完全相同,這時必須把問題作一個小的變化,使得遞歸子問題與原問題完全相同。例5.8【例5.8】用遞歸方法求解斐波納契(Fibonacci)數列。分析問題:斐波納契數列:0,1,1,2,3,5,8,13,21,34,…。第n項的遞歸公式為:fib(n)=n(n=0,1)/*遞歸結束條件*/fib(n-2)+fib(n-1)(n>1)/*遞歸方式*/第n項斐波納契數列值是前兩項的和,這是遞歸方式;而當n=0和1時,斐波納契數列就是n值本身,這是遞歸結束條件。例5.8程序#include<stdio.h>longfib(intn);/*自定義函數聲明*/voidmain(){longs;/*第i項斐波納契數列的值*/inti=0;/*斐波納契數列某項的序號*/do{printf("InputFibonacciNumber:");scanf("%d",&i);/*輸入要求的某一項的序號*/s=fib(i);/*求斐波納契數列第i項*/printf("Fib(%d)=%ld\n",i,s);}while(i>0);/*循環輸入序號,直到i值小于等于0*/}/*定義求第n項斐波那契數列值的函數*/longfib(intn){if(n==0||n==1)/*判斷是否為結束條件*/returnn;elsereturnfib(n-2)+fib(n-1);/*求斐波納契數列的遞歸方式*/}變量的作用域局部變量——在語句塊(一對花括號括起來的區域)內定義,僅在定義它的語句塊內有效,并且擁有自己獨立的存儲空間全局變量——在函數之外定義的變量,可以在本文件的所有函數中使用,有效范圍從定義變量的位置開始到文件結束說明在一個函數中既可以使用本函數中的局部變量,又可以使用有效的全局變量可以利用全局變量增加與函數聯系的渠道,從函數得到一個以上的返回值。全局變量相當于各個函數間的傳遞通道為了便于區別全局變量和局部變量,C語言中有一個不成文的規定,即將全局變量名的第一個字母用大寫表示編程時一般不要使用全局變量,因為全局變量一直占用存儲空間,降低了函數的通用性,降低了程序的清晰性,易出錯變量的存儲空間變量是對程序中數據的存儲空間的抽象內存…….voidmain(){inta;a=10;printf(“%d”,a);}編譯或函數調用時為其分配內存單元1020002001程序中使用變量名對內存操作變量的屬性變量的屬性包括操作屬性和存儲屬性。操作屬性以數據類型形式表現出來。存儲屬性即存儲器類型,它說明變量占用存儲空間的區域。數據類型:變量所持有的數據的性質(操作屬性)存儲屬性:存儲器類型:寄存器、靜態存儲區、動態存儲區生存期:變量在某一時刻存在-------靜態變量與動態變量作用域:變量在某區域內有效-------局部變量與全局變量變量的作用域變量的作用域是指變量的可見范圍或可使用的有效范圍,即變量的“可見性”。變量的作用域可以在一個函數范圍內,也可以在整個程序范圍內。變量可以在程序中三個地方說明:函數內部、函數的參數定義中或所有函數的外部。函數內部定義的變量、函數的參數變量、被一對大括號{}括起來的區域(語句塊)定義的變量等均為局部變量,函數外部定義的變量則為全局變量。局部變量、全局變量等的作用域是不一樣的。局部變量的作用域舉例#include<stdio.h>voidmain(){inta,b;…{intc;c=a+b;…}…}變量c的作用范圍變量a、b的作用范圍全局變量的作用域舉例#include<stdio.h>floatf1(inta);/*函數聲明*/intx=1,y=5;/*定義全局變量*/voidmain(){intm,n;/*定義局部變量*/m=x+10;…}floatf2(inta,intb);/*函數聲明*/floatf1(inta)/*定義函數,函數參數是局部變量*/{intb,c;/*定義局部變量*/…}charc1,c2;/*定義全局變量*/floatf2(inta,intb)/*定義函數,函數參數是局部變量*/{inti,j,x;/*定義局部變量*/x=6;…}floats1,s2;/*定義全局變量*/全局變量c1、c2的作用范圍全局變量x、y的作用范圍例5.9【例5.9】輸入正方體的長l、寬w、高h,求體積及三個不同面的面積。分析問題:可以定義s1、s2、s3三個全局變量,分別表示三個不同面的面積,并在求體積vs()函數中一并計算,然后在主函數main()中輸出。例5.9程序#include<stdio.h>ints1,s2,s3;/*定義全局變量s1、s2、s3*/intvs(inta,intb,intc);/*自定義函數聲明*/intmain(){intv,l,w,h;/*定義局部變量v、l、w、h*/printf("\ninputlength,widthandheight\n");scanf("%d,%d,%d",&l,&w,&h);v=vs(l,w,h);printf("v=%d,s1=%d,s2=%d,s3=%d\n",v,s1,s2,s3);return(0);}intvs(inta,intb,intc){intv;/*在vs()函數中定義局部變量v*/v=a*b*c;s1=a*b;/*自定義函數中引用s1、s2、s3*/s2=b*c;s3=a*c;return(v);}變量的存儲類型從變量的作用域角度來分,變量可以分為全局變量和局部變量而從變量值存在的時間(即生存期)角度來分,變量可以分為靜態存儲方式和動態存儲方式靜態存儲方式是指程序運行期間分配固定的存儲空間,該空間從分配時刻開始存在,直到程序運行結束才釋放動態存儲方式是指程序運行期間根據需要動態分配存儲空間,該空間使用結束后自動釋放,下次需要時再分配存儲類型的提出下面程序的最后定義全局變量A、B有意義嗎?要使它能使用,必須通過通過externintA,B;語句擴展其作用域。這就是變量的存儲類型所要做的事情(參見例5.11)。#include<stdio.h>intmax(intx,inty){intz;z=x>y?x:y;return(z);}voidmain(){printf("max=%d",max(a,b));}intA=13,B=-8;externintA,B;變量A、B本程序中無效變量A、B的有效范圍存儲類型的分類與定義變量的存儲類型分類auto-----自動型register-----寄存器型static------靜態型extern-----外部型變量定義格式:[存儲類型]數據類型變量表;如:intsum;

autointa,b,c;

registerinti;

staticfloatx,y;動態變量與靜態變量動態變量與靜態變量,是從變量的生存期角度來分類的。1.存儲方式靜態存儲:程序運行期間分配固定存儲空間。動態存儲:程序運行期間根據需要動態分配存儲空間。2.內存用戶區程序區靜態存儲區動態存儲區全局變量、局部靜態變量形參變量局部動態變量(autoregister)函數調用現場保護和返回地址等3.生存期靜態變量:從程序開始執行到程序結束。動態變量:從包含該變量定義的函數開始執行至函數執行結束。自動變量auto在C語言中,函數內部定義的變量(局部變量)默認為自動變量(automaticvariable)。關鍵字auto能夠顯式地指定存儲類別。顯式定義

autointx,y;隱含定義等價intx,y;關鍵字“auto”可以省略,auto不寫則隱含確定為“自動存儲類別”,即動態存儲方式。程序中大多數變量屬于自動變量。auto的作用域main(){intx=1;voidprt(void);{intx=3;prt();printf(“2ndx=%d\n”,x);}printf(“1stx=%d\n”,x);}voidprt(void){intx=5;printf(“3thx=%d\n”,x);}運行結果:3thx=52ndx=31stx=1x=1作用域x=1作用域x=3作用域x=5作用域寄存器變量register

對于使用頻繁的變量,可以使用register聲明為寄存器變量,其值存儲在CPU中,加快了運行速度。如:registerintx;registercharc;說明:①只有局部自動變量和形式參數可以作為寄存器變量,其余非法。②一個計算機系統中的寄存器數目是有限的,不能定義任意多個寄存器變量。③一般來講只允許int和char才能定義為寄存器變量,但目前大多數系統都支持指針型變量定義為寄存器變量。④局部靜態變量不能定義為寄存器變量,即不能定義:registerstaticintx;,對一個變量只能聲明一個存儲類別。⑤當今優化的編譯系統,能夠自動識別使用頻繁的變量,從而自動的將這些變量放在寄存器中。因此程序設計者實際上根本不需要去聲明寄存器變量。用static聲明局部變量

有時希望函數中的局部變量的值在函數調用結束后不消失而保留原值,以便下一次調用該函數時可以使用上一次調用的最后結果。這時就應該指定該局部變量為“靜態局部變量”。#include<stdio.h>voidmain(){voidincrement(void);increment();increment();increment();}voidincrement(void){intx=0;x++;printf(“%d\n”,x);}運行結果:111#include<stdio.h>voidmain(){voidincrement(void);increment();increment();increment();}voidincrement(void){staticintx=0;x++;printf(“%d\n”,x);}運行結果:123例:局部靜態變量值具有可繼承性例5.10【例5.10】用靜態變量實現計算1~5的階乘的程序。分析問題:

n!=1*2*…*(n-1)*n,可以依次計算1!=1,2!=1!*2,3!=2!*3,…,n!=(n-1)!*n。也就是說,計算n!需要使用前面的(n-1)!值,而(n-1)!值可以使用靜態變量來存儲。例5.10程序#include<stdio.h>intfac(inti);/*計算階乘的函數聲明*/intmain(){inti,r;/*如果i值頻繁使用,編譯器會自動以寄存器變量存放*/for(i=1;i<=5;i++){r=fac(i);/*調用計算i的階乘函數*/printf("%d!=%d\n",i,r);}return(0);}/*定義計算i的階乘的函數*/intfac(inti){staticintf=1;/*定義靜態變量f存放階乘值,第一次調用時賦初值*/if(i>=1)f=f*i;/*f的初值為上次計算的f值*/return(f);}用extern聲明外部變量外部變量(即全局變量)是在函數的外部定義的,它的作用域為從變量的定義處開始,到本程序文件的末尾。在此作用域內,全局變量可以為程序中各個函數所引用。編譯時將外部變量分配在靜態存儲區。有時需要用extern來聲明外部變量,以擴展外部變量的作用域。如果全局變量在后面定義,而在前面的函數中要使用,則必須在使用前用extern聲明該全局變量。如:externintAbc;

也可以省略int成:externAbc;全局變量的定義與使用extern聲明的主要區別

項目全局變量的定義使用extern聲明次數只能1次可聲明多次位置所有函數之外函數內或函數外分配內存分配內存

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論