第7章函數_第1頁
已閱讀1頁,還剩66頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、第7章 函數,C語言程序設計,本章學習內容,? 函數定義、函數調用、函數原型、函數的參數傳遞與返回值? 遞歸函數和函數的遞歸調用? 函數封裝,函數復用,函數設計的基本原則,程序的健壯性? 變量的作用域與存儲類型,全局變量、自動變量、靜態(tài)變量、寄存器變量? “自頂向下、逐步求精”的模塊化程序設計方法,數學中的函數,自變量,因變量,函數名,程序設計中的函數,程序設計中的函數不局限于計算計算類,如打印階乘表的程序……判斷

2、推理類,如排序、查找……,問題的提出,讀多少行的程序能讓你不頭疼?假如系統(tǒng)提供的函數printf()由10行代碼替換,那么你編過的程序會成什么樣子?實際上一個printf()有上千行代碼main()中能放多少行代碼?如果所有代碼都在main()中,怎么團隊合作?如果代碼都在一個文件中,怎么團隊合作?,問題的提出,《三國演義》中有這樣一段描寫:懿問曰:“孔明寢食及事之煩簡若何?”使者曰:“丞相夙興夜寐,罰二十以上皆親覽焉。所啖

3、之食,日不過數升?!避差欀^諸將曰:“孔明食少事煩,其能久乎?”此話音落不久,諸葛亮果然病故于五丈原?!笆聼o巨細”,“事必躬親” 管理學的觀點是極其排斥這種做法的,認為工作必須分工,各司其職其中的思想,在程序設計里也適用,7.1分而治之與信息隱藏,分而治之( Divide and Conquer,Wirth, 1971 )函數把較大的任務分解成若干個較小的任務,并提煉出公用任務信息隱藏(Information Hiding,

4、 Parnas, 1972)設計得當的函數可把具體操作細節(jié)對外界隱藏起來,從而使整個程序結構清楚使用函數時,不用知道函數內部是如何運作的,只按照我們的需要和它的參數形式調用它即可,程序設計的藝術,算法設計藝術程序的靈魂Donald E. Knuth,“The Art of Computer Programming”, 清華大學出版社(英),國防工業(yè)出版社(中)結構設計藝術程序的肉體模塊化(Parnas, 1972)

5、結構化(Structural)面向對象(Object-Oriented)面向組件(Component-Oriented)面向智能體(Agent-Oriented)……,函數是C語言中模塊化編程的最小單位可以把每個函數看做一個模塊( Module )如把編程比做制造一臺機器,函數就好比其零部件可將這些“零部件”單獨設計、調試、測試好,用時拿出來裝配,再總體調試。這些“零部件”可以是自己設計制造/別人設計制造/現成的標準產品

6、,7.2 函數的定義,7.2 函數的定義,若干相關的函數可以合并成一個“模塊”一個C程序由一個或多個源程序文件組成一個源程序文件由一個或多個函數組成,,7.2.1函數的分類,函數生來都是平等的,互相獨立的,沒有高低貴賤和從屬之分main( )稍微特殊一點點C程序的執(zhí)行從main ( )函數開始調用其他函數后流程回到main ( )函數在main ( )函數中結束整個程序運行,7.2.1函數的分類,標準庫函數ANSI/ISO

7、 C定義的標準庫函數符合標準的C語言編譯器必須提供這些函數函數的行為也要符合ANSI/ISO C的定義第三方庫函數由其他廠商自行開發(fā)的C語言函數庫不在標準范圍內,能擴充C語言的功能(圖形、網絡、數據庫等)自定義函數自己定義的函數包裝后,也可成為函數庫,供別人使用,7.2.2函數的定義(Function Definition),類型 函數名(類型 參數1, 類型 參數2, ……){聲明語句序列

8、 可執(zhí)行語句序列 return 表達式;},返回值類型,函數名標識符,說明運算規(guī)則,參數表相當于運算的操作數,,返回運算的結果,函數出口,類型 函數名(類型 參數1, 類型 參數2, ……){ 聲明語句序列 可執(zhí)行語句序列 return 表達式;},,參數表里的變量(叫形式參數,Formal Parameter)也是內部變量,,函數體,7.2.2函數的定義(Function Defini

9、tion),void 函數名(void){聲明語句序列 可執(zhí)行語句序列 return;},函數無返回值,用void定義返回值類型,用void定義參數,表示沒有參數,return語句后無需任何表達式,7.2.2函數的定義(Function Definition),【例7.1a】 計算整數n的階乘n!,/* 函數功能: 用迭代法計算n! 函數入口參數: 整型變量n表示階乘的階數 函數返回

10、值: 返回n!的值*/long Fact(int n) /* 函數定義 */{ int i; long result = 1; for (i=2; i<=n; i++) { result *= i; } return result; },,,,,,,返回值類型,函數名說明函數的功能,返回值作為函數調用表達式的值,,形參

11、表,函數入口,函數內部可以定義只能自己使用的變量,稱內部變量,函數名(表達式1, 表達式2, ……);實際參數(Actual Argument )函數調用(Founction Call)時提供的表達式有返回值時放到一個數值表達式中 c = max(a,b);作為另一個函數調用的參數 c = max(max(a,b),c); printf("%d\n", max(a,b));無返回值時函數

12、調用表達式 display(a,b);,7.3向函數傳遞簡單變量的值和從函數返回值,函數的參數傳遞,實參和形參必須匹配數目一致,類型一一對應(否則會發(fā)生自動類型轉換),【例7.1】,7.3.2函數原型(Function Prototype),在調用函數前先聲明其返回值類型、函數名和參數函數原型有助于編譯器對函數參數類型的匹配檢查,,末尾有一個分號,聲明時不要省略形參和返回值的類型,【例7.1】,,函數定義與函數聲明的區(qū)別,函數

13、定義指函數功能的確立指定函數名、函數類型、形參及類型、函數體等是完整獨立的單位 函數聲明是對函數名、返回值類型、形參類型的說明不包括函數體是一條語句,以分號結束,只起一個聲明作用,7.3.3函數封裝與防御性程序設計,函數封裝(Encapsulation)使得外界對函數的影響僅限于入口參數,而函數對外界的影響僅限于一個返回值和數組、指針類型的參數,【例7.1】,Why?,傳入負數實參會怎樣?,防御性程序設計(Defensi

14、ve Programming),如何使函數具有遇到不正確使用或非法數據輸入時避免出錯的能力,增強程序的健壯性? 在函數的入口處,檢查輸入參數的合法性,【例7.2】 計算整數n的階乘n!,,如何使函數具有遇到不正確使用或非法數據輸入時避免出錯的能力,增強程序的健壯性? 在函數的入口處,檢查輸入參數的合法性,,,,防御性程序設計(Defensive Programming),【例7.2】計算整數n的階乘n!,主函數如何修改?增加對函數

15、返回值的檢驗,,,,防御性程序設計(Defensive Programming),【例7.3】計算整數n的階乘n!,傳入負數的實參時Fact()會返回-1嗎?存在死代碼的原因何在?,,防御性程序設計(Defensive Programming),【例7.3】計算整數n的階乘n!,,如何修改程序去除冗余代碼?如何保證不會傳入負數實參?,防御性程序設計(Defensive Programming),【例7.2】計算整數n的階乘n!,【例

16、7.4】編寫計算組合數的程序,,函數復用,7.3.4函數設計的基本原則,信息隱藏,入口參數有效性檢查敏感操作前的檢查調用成功與否的檢查,函數的嵌套調用,嵌套調用在調用一個函數的過程中,又調用另一個函數C語言規(guī)定函數不能嵌套定義,但可以嵌套調用函數是相互平行的,main(){……a();},,a 函數{b();…return;},,,b函數{……return;},,,①,③,④,⑤,,⑥

17、,,⑦,②,,,7.4 函數的遞歸調用和遞歸函數,如果一個對象部分地由它自己組成或按它自己定義,則我們稱它是遞歸(Recursive)的。生活中,字典就是一個遞歸問題的典型實例字典中的任何一個詞匯都是由“其他詞匯”解釋或定義的,但是“其他詞匯”在被定義或解釋時又會間接或直接地用到那些由它們定義的詞遞歸方法的基本原理將復雜問題逐步化簡,最終轉化為一個最簡單的問題最簡單問題的解決就意味著整個問題的解決,遞歸函數(Recursive

18、 Function),long fact(int n) { if (n < 0) return -1; else if (n == 0 || n == 1) return 1; else return n * fact(n-1); },,,【例7.5】計算n!= n *(n-1)*(n-2)*……*1,函數直接或間接調用自己,稱為遞歸調用(Recursive

19、Call),遞歸函數(Recursive Function),unsigned long fact(unsigned int n) { if (n == 0 || n == 1) return 1;else return n * fact(n-1); },,,,基線情況(base case),一般情況(general case),無需考慮n<0了,【例7.5】計算n!= n *(n-1)*(n-2)*…*1

20、,遞歸函數(Recursive Function),遞歸調用應該能夠在有限次數內終止遞歸遞歸調用若不加以限制,將無限循環(huán)調用必須在函數內部加控制語句,僅當滿足一定條件時,遞歸終止,稱為條件遞歸任何一個遞歸調用程序必須包括兩部分遞歸循環(huán)繼續(xù)的過程遞歸調用結束的過程,if (遞歸終止條件成立) return 遞歸公式的初值; else return 遞歸函數調用返回的結果值;,n!=n×(n-1)

21、! (n-1)!=(n-1)×(n-2)! (n-2)! … (n-3)! 5! … 4!=4×3! 3!=3×

22、;2! 2!=2×1! 1!=1,,,回推過程,遞推過程,每個遞歸函數必須至少有一個基線條件一般情況必須最終能簡化為基線條件,遞歸層數太多易導致??臻g溢出后果很嚴重,程序被異常中止,fact(5)=5*fact(4)= 120 fact(4)= 4*fact(3)= 24 fact(3)= 3*f

23、act(2)= 6 fact(2)= 2*fact(1)=2 fact(1)=1,,main,fact(5),fact(4),fact(3),fact(2),fact(1),遞歸與迭代,用迭代(即循環(huán))方法編寫的階乘函數unsigned long Fact(unsigne

24、d int n){ unsigned long result = 1; unsigned int i; for (i = 1; i <= n; i++) result *= i; return result;}遞歸程序遵循了數學中對階乘的定義因此遞歸方法編寫程序具有更清晰、可讀性更好的優(yōu)點,遞歸與迭代,1,1,2,3,5,8,......,long Fib(int n){long

25、f;if (n == 0) f = 0;else if (n == 1) f = 1;else f = Fib(n-1) + Fib(n-2);return f;},【例7.6】計算Fibonacci數列,遞歸與迭代,優(yōu)點:從編程角度來看,比較直觀、精煉,邏輯清楚符合人的思維習慣,逼近數學公式的表示尤其適合非數值計算領域hanoi塔,騎士游歷、八皇后問題(回溯法)缺點:增加了函數調用的開銷,每次調用都需要

26、進行參數傳遞、現場保護等耗費更多的時間和??臻g應盡量用迭代形式替代遞歸形式,7.5變量的作用域和存儲類型,7.5.1變量的作用域 ( Scope )指在源程序中定義變量的位置及其能被讀寫訪問的范圍分為局部變量(Local Variable) 全局變量(Global Variable ),局部變量( Local Variable ),在語句塊內定義的變量形參也是局部變量特點生存期是該語句塊,進入語句塊時獲得內存,僅能由語

27、句塊內語句訪問,退出語句塊時釋放內存,不再有效定義時不會自動初始化,除非程序員指定初值并列語句塊各自定義的同名變量互不干擾 形參和實參可以同名,全局變量( Global Variable ),在所有函數之外定義的變量生存期是整個程序,從程序運行起占據內存,程序運行過程中可隨時訪問,程序退出時釋放內存有效范圍是從定義變量的位置開始到本程序結束,全局變量( Global Variable ),【例7.7】打印計算Fibonacci

28、數列每一項時所需的遞歸調用次數,,全局變量使函數間的數據交換更容易,更高效,但建議盡量少用,因為誰都可改寫它,所以很難確定是誰改寫了它,,7.5.2變量的存儲類型( Storage Class),指數據在內存中存儲的方式即編譯器為變量分配內存的方式,它決定變量的生存期 存儲類型 數據類型 變量名;C程序的存儲類別auto型(自動變量)static型(靜態(tài)變量)extern型(外部變量)r

29、egister型(寄存器變量),,,靜態(tài)存儲區(qū)中的變量:與程序“共存亡” 動態(tài)存儲區(qū)中的變量:與程序塊“共存亡” 寄存器中的變量: 同動態(tài)存儲區(qū),變量的生存期(Lifetime )決定何時“生”,何時“滅”,,7.5.2變量的存儲類型( Storage Class),auto 數據類型 變量名;auto體現在進入語句塊時自動申請內存,退出時自動釋放內存動態(tài)局部變量,缺省的存儲類型靜

30、態(tài)變量 static 數據類型 變量名;static storage class for local variables (declared inside a block or function) - the lifetime of the entire program生存期為整個程序運行期間,自動變量和靜態(tài)變量,,自動變量和靜態(tài)變量,【例7.9】,自動變量不初始化時,值是隨機值,靜態(tài)局部變量和全局變量自動初始化為0值

31、,【例7.10】利用靜態(tài)變量計算整數n的階乘n!,,自動變量和靜態(tài)變量,靜態(tài)變量僅初始化一次,變量的值可保存到下次進入函數,使函數具有記憶功能,寄存器變量,寄存器CPU內部容量有限、但速度極快的存儲器 register 類型名 變量名; 使用頻率比較高的變量聲明為register ,可使程序更小、執(zhí)行速度更快現代編譯器有能力自動把普通變量優(yōu)化為寄存器變量,并且可以忽略用戶的指定所以一般無須特別聲

32、明變量為register,全局變量,,靜態(tài)外部變量 (只限本文件使用),外部變量 (非靜態(tài)外部變量,允許其他文件引用),局部變量,,自動變量,(離開函數,值就消失),寄存器變量(離開函數,值就消失),,定義點之前使用,需用extern聲明,靜態(tài)局部變量 (離開函數,值仍保留),動態(tài)局部變量,,7.5變量的作用域和存儲類型,7.6模塊化程序設計,模塊各司其職每個模塊只負責一件事情,它可以更專心便于進行單個模塊的設計、開發(fā)、調試、測

33、試和維護等工作一個模塊一個模塊地完成,最后再將它們集成開發(fā)人員各司其職按模塊分配任務,職責明確并行開發(fā),縮短開發(fā)時間什么時候需要模塊化?某一功能,如果重復實現3遍以上,即應考慮模塊化,將它寫成通用函數,并向小組成員發(fā)布,7.6模塊化程序設計,模塊化的優(yōu)點——復用構建新的軟件系統(tǒng)可以不必每次從零做起,直接使用已有的經過反復驗證的軟構件,組裝或加以合理修改后成為新的系統(tǒng),提高軟件生產率和程序質量在其他函數的基礎上構造程序拿

34、來拿去主義,指盡可能復用其他人現成的模塊不是人類懶惰的表現,而是智慧的表現一般要靠日積月累才能建立可以被復用的軟件庫前期投入多,缺乏近期效益,大部分公司都注重近期效益,是為了生存,所以軟件復用對公司來說不是最高優(yōu)先級,7.6模塊化程序設計,功能分解自頂向下、逐步求精的過程模塊分解的基本原則保證模塊的相對獨立性——高聚合、低耦合模塊的實現細節(jié)對外不可見——信息隱藏外部:關心做什么;內部:關心怎么做設計好模塊接口接口指羅

35、列出一個模塊的所有的與外部打交道的變量等 定義好后不要輕易改動在模塊開頭(文件的開頭)進行函數聲明,7.6模塊化程序設計,逐步求精(Stepwise Refinement)由不斷的自底向上修正所補充的自頂向下(Top-down)的程序設計方法,【例7.11】用函數完成猜數游戲,猜多個數,10次猜不對就猜下一個數模塊分解過程,開始,結束,初始化,退出處理,主功能,為程序運行所做的準備工作,在退出前要做的事情,如打印結果、資源釋放

36、等,自底向上,自頂向下的模塊化程序設計,,【例7.11】用函數完成猜數游戲,,開始,結束,生成數字,猜數字,【例7.11】用函數完成猜數游戲,開始,結束,生成數字,猜數字,是否繼續(xù)?,N,Y,,,,【例7.11】用函數完成猜數游戲,開始,結束,猜得對嗎?,N,Y,提示大小,次數<10?,輸入數字,,N,,Y,,處理用戶輸入,判斷是否有輸入錯誤,是否在合法的數值范圍內,【例7.11】用函數完成猜數游戲,,【例7.11】用函數完成猜

37、數游戲,,,,【例7.11】用函數完成猜數游戲,,,,,,【例7.11】用函數完成猜數游戲,int MakeNumber(void){ int number; number = (rand() % (MAX_NUMBER - MIN_NUMBER + 1) ) + MIN_NUMBER; assert(number >= MIN_NUMBER && number <= MAX_NU

38、MBER); return number;},使用斷言(Assert)防止某些參數獲得非法值,在程序調試和測試時發(fā)現錯誤,#include void assert(int expression);expression為真,無聲無息;為假,中斷程序斷言僅用于調試程序,不能作為程序的功能,斷言,用來測試某種不可能發(fā)生的狀況確實不會發(fā)生Debug版有效Release版失效考慮使用斷言的幾種情況檢查程序中的各種假設的正確

39、性證實或測試某種不可能發(fā)生的狀況確實不會發(fā)生,程序版式,縮進(Indent)—保證代碼整潔、層次清晰的主要手段良好風格的程序應嚴格采用梯形層次對應好各層次,int IsPrime(int n){ int k, i; k = sqrt((double)n); for (i=2; i<=k; i++) { if (n % i == 0) retu

40、rn 0; } return 1;},#include main(){ int i; for (i=2; i<100; i++) { if (IsPrime(i)) printf("%d\t",i); }},,程序版式,現在的許多開發(fā)環(huán)境、編輯軟件都支持自動縮進根據用戶代碼的輸入,智能判斷應該縮進還是反縮進,替用戶完

41、成調整縮進的工作VC中有自動整理格式功能只要選取需要的代碼,按ALT+F8就能自動整理成微軟的cpp文件格式,命名規(guī)則,在Linux/UNIX平臺習慣用function_name 本書采用Windows風格函數名命名用大寫字母開頭、大小寫混排的單詞組合而成 FunctionName 變量名形式“名詞”或者“形容詞+名詞”如oldValue與newValue等函數名形式“動詞”或者“動詞+名詞”(動賓詞組)如Get

42、Max()等,對函數接口進行注釋說明,/* 函數功能:實現××××功能 函數參數:參數1,表示×× 參數2,表示×× 函數返回值: ×××××*/返回值類型 函數名(形參表){…return 表達式;},,,,,挑戰(zhàn)性的作業(yè)挑戰(zhàn)類型表示的極限 ——50位

溫馨提示

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

評論

0/150

提交評論