《C語言程序設計》課件第4章_第1頁
《C語言程序設計》課件第4章_第2頁
《C語言程序設計》課件第4章_第3頁
《C語言程序設計》課件第4章_第4頁
《C語言程序設計》課件第4章_第5頁
已閱讀5頁,還剩82頁未讀 繼續免費閱讀

下載本文檔

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

文檔簡介

第4章函數4.1函數的定義

4.2函數參數與返回值

4.3函數調用

4.4函數的嵌套調用4.5變量的作用域4.6變量的存儲類別

4.1函?數?的?定?義

4.1.1函數的基本概念

C程序中有一個函數是必須存在的,這就是main函數(又稱為主函數)。main函數是唯一的,它是C程序執行的入口,即程序開始執行時,系統首先調用main函數執行。

C程序中所有函數的定義是平行的,函數之間不存在嵌套或從屬的關系,但是函數之間可以相互調用。除main函數不能被其他函數調用外,其他函數都是一個可以反復使用的程序段。函數是通過被調用而執行的。

【例4.1】

編寫程序計算兩個整數絕對值階乘之差。

程序如下:圖4.1輸入-7和2時的運行結果例4.1的程序由main和fac兩個函數組成。其中fac函數的功能是求整數的階乘。程序的執行從main函數開始,首先調用函數printf輸出提示信息,調用函數scanf輸入數據。然后先后兩次調用函數fac:第一次調用fac函數時,由main函數轉到fac函數執行,計算x絕對值的階乘,函數執行完后,結果由return語句返回main函數;第二次調用fac函數計算y絕對值的階乘。最后調用函數printf輸出計算結果,程序執行結束。在執行函數fac時還調用了函數abs計算n的絕對值。

從上面的例子可以看出,函數是有區別的。有一種函數可以直接使用,例如例4.1中的scanf、printf和abs函數,它們是由C系統提供的,稱做C系統的系統函數或庫函數。還有一種函數用戶必須定義(即創建)后才能使用,例如例4.1中的fac函數,稱做用戶自定義函數。使用C語言編程,一定要了解有哪些系統函數才能有效利用系統提供的資源。

4.1.2函數的定義

編寫C程序的主要工作就是編寫用戶自定義函數,即所謂的函數定義。函數定義的一般形式如下:

類型標識符函數名(形式參數表列)

{??聲明部分

執行部分

}輸入“23,4”時,程序運行結果如圖4.2所示。

編寫函數時,函數名要符合標識符的命名規則,例如,start_information和end_information。關于函數有如下說明:

(1)函數定義時的參數稱為形式參數,簡稱形參。形式參數列表說明的是函數間要傳遞的數據。調用函數與被調用函數之間的數據傳遞就是依靠形式參數在調用時接收數據來完成的。圖4.2例4.2的運行結果

(2)形式參數列表由各個參數的名字和類型說明組成。形參列表中若有多個形參,形參之間用逗號分隔。例如,“intsum(intx1,intx2)”說明sum函數有兩個整型參數x1,x2,即調用函數時需要傳遞的數據是兩個整型數據。

(3)如果在形式參數列表中只列出參數名,則需要在其后說明每個參數的類型,函數定的形式變為

(4)如果函數不需要數據傳遞,則形式參數表為空,但“()”不能省略。例如start_information()和end_information()。

(5)花括號內的內容稱為“函數體”。聲明部分對函數內使用的變量進行定義和聲明,以及對被調用函數進行的聲明。執行部分是實現函數功能的語句序列。

4.2函數參數與返回值

4.2.1函數的參數

函數的參數分為形參和實參兩種。形參出現在函數定義中,在整個函數體內都可以使用,若離開該函數則不能使用;實參出現在主調函數中。形參和實參的功能是作數據傳送,發生函數調用時,主調函數把實參的值傳送給被調函數的形參,從而實現主調函數向被調函數的數據傳送。

函數的形參和實參具有以下特點。

(1)形參變量只有在被調用時才分配內存單元,在調用結束時,即刻釋放所分配的內存單元。因此形參只有在函數內部有效,函數調用結束返回主調函數后,就不能再使用該形參變量。

(2)實參可以是常量、變量、表達式、函數等,無論實參是何種類型的量,在進行函數調用時,它們都必須具有確定的值,以便把這些值傳送給形參。因此應預先用賦值、輸入等辦法使實參獲得確定值。

(3)實參和形參在數量、類型、順序上應嚴格一致,否則會發生“類型不匹配”的錯誤。

(4)函數調用中發生的數據傳送是單向的,即只能把實參的值傳送給形參,而不能把形參的值反向地傳送給實參。因此在函數調用過程中,形參的值發生改變對實參的值不會有影響。圖4.3例4.3運行結果4.2.2函數的返回值

函數的返回值是指函數被調用后,執行函數體的程序段所取得并返回給主調函數的值,如調用正弦函數取得的正弦值、調用max函數取得的最大值等。

(1)函數的值只能通過return語句返回主調函數。

return語句格式為

return(<表達式>);

return語句有如下兩個功能:

①return語句將表達式的計算結果返回給主調函數;

②結束return語句所在函數的執行,返回到調用該函數的主調函數中繼續執行。圖4.4例4.4的運行結果程序說明:程序執行到product函數的return語句時,return語句將s的值返回到main函數,在結束product函數的執行后,返回main函數繼續執行。return語句返回的s值在main函數中賦值給了變量p。

(2)函數返回值的類型應為定義函數時函數的類型。若函數類型與return語句中表達式值的類型不一致,則以函數類型為準進行類型轉換。例如,如果product函數為

return語句返回值是float類型,而函數類型為double,則將返回值轉換成double類型返回。

(3)沒有返回值的函數用void定義其函數類型。否則,函數即使不用return語句返回值,函數仍將返回一個不確定的值。如例4.2中的start_information和end_information函數。

(4)如果return語句中沒有返回值,語句格式為

return;

(5)?return語句可以出現多次,但是每次函數調用只能有一條return語句被執行。例如下面的example函數: 4.3函數調用

4.3.1函數的聲明

在C程序中,一個函數可以被調用需要以下兩個前提。

(1)函數已存在。或者函數是系統函數(庫函數),或者函數是用戶已定義完的函數。

(2)對于庫函數,只要在主調函數所在的文件用include命令包含相應的頭文件即可;對于用戶自定義的函數,除下面將要提到的三種情況外都需要在調用函數前對其進行聲明,或者在主調函數所在文件用include命令包含被調用函數的所在文件。所謂函數定義,是指編寫不存在的函數,函數只能定義一次。而函數聲明是對存在的準備調用的函數進行聲明,讓編譯系統可以在編譯階段對函數的調用進行合法性檢查,判斷形參與實參的類型及個數是否一致,函數聲明可以多次。

函數聲明格式就是照寫已定義的函數首部,再加上分號,即

類型標識符函數名(形式參數表列);輸入“1.5,1.2”時,結果如圖4.5所示。

C語言規定,在下列三種情況下,調用函數前可以不對被調用函數進行聲明:

(1)被調函數的返回值類型是整型或字符型。例如例4.1,被調用函數fac的返回值是整型,main函數可不必聲明它而直接調用。

(2)在函數的外部已對該函數聲明,且聲明在主調函數的前面。如例4.5。圖4.5例4.5的運行結果圖4.6例4.6的運行結果

(3)被調用函數的定義和調用函數在同一個文件內,且出現在調用函數之前。例如例4.3,函數product出現在函數main之前,則main函數可以不對product函數聲明而直接調用。

調用一個函數前進行函數聲明是一種良好的程序設計習慣,除了增加程序的正確性,把錯誤改正在初級階段外,還能提高程序的可讀性。

4.3.2函數的調用

函數的調用就是主調函數通過數據傳遞使用被調用函數功能的過程。數據傳遞是通過形參和實參完成的。

函數調用的一般形式是:

函數名(實際參數表列)說明:

(1)調用函數時的參數稱為實際參數,簡稱實參。實參可以是變量、常量或表達式,是有確定值的參數。

(2)函數的形參與實參要求個數相等,并且對應的形參和實參的類型相同。若被調函數是無參函數,則實參表列為空。

(3)數據傳遞是通過形參接收實參的數值來完成的。函數調用時,形參被分配內存單元,并接收對應實參傳來的值。

【例4.7】

將三個實數按由小到大順序輸出并且輸出它們的和。

程序如下:圖4.7例4.7運行結果例4.7的main函數執行過程中依次調用了函數start_information、函數sort和函數end_information。start_information和end_information是無參數、無返回值函數,被main調用時無需數據傳遞,執行完畢返回main函數執行即可。sort函數是既有參數,也有函數返回值的函數。sort函數的調用過程是一個完整的函數調用過程,其調用過程具體如下:

(1)為函數的形參x1、x2、x3分配內存單元,計算各個實參表達式a+1、3.0、a的值為5.0、3.0、4.0,并一一對應地賦值給相應形參x1、x2、x3。

(2)進入sort函數體,執行函數中的語句,實現函數的排序輸出功能。

(3)執行到return語句時,計算return語句中表達式x3+x2+x1的值并向主調函數返回其值,釋放形參及sort函數內的變量所占內存,返回main函數繼續執行。

函數調用可以有不同的方式,例如例4.8。圖4.8例4.8的運行結果在例4.8中出現了三種函數調用方式,也就是一般函數的三種調用方式:

(1)具有返回值的函數可參加表達式的計算。例如例4.8的語句“t=max(x,y)+10;”中的max函數。

(2)具有返回值的函數可作為其他函數的實參。例如例4.8的語句“printf("\nthemaximumis:%f",max(t,z));”中,max函數作為printf函數中的一個參數;

(3)只完成一定功能而沒有返回值的函數可單獨成為一個語句,例如例4.8的語句:

end_information();

程序說明:若是按自左向右順序求值的系統,輸出結果應該是:44。所以該編譯系統是按自右向左順序求值的。圖4.9例4.9的運行結果4.3.3函數調用的數據傳遞方式

C語言中,函數的實參和形參之間的數據傳遞是單方向的值傳遞方式。只能將實參的值傳遞給形參,而不能將形參的值傳遞給實參。形參值的變化不影響實參。

當調用函數時,給形參分配內存單元,將實參的值賦給形式參數。函數執行完后,形參占用的空間被釋放,實參仍為調用前的值。

【例4.10】

形參值的變化不影響實際參數的值。

程序如下:

程序說明:main函數調用函數change時,系統給形參x、y分配空間,將實參a的值賦給形參x,實參b的值賦給形參y。執行change函數時,x、y重新被賦值。change函數執行完x、y占用的空間被釋放。形參x、y的變化影響不到實參a、b,因此main函數內輸出為“a=3,b=4”。圖4.10例4.10的運行結果

4.4函數的嵌套調用

4.4.1函數的嵌套調用

C語言中,函數不允許嵌套定義,但可以嵌套調用,即在調用一個函數的過程中,被調用函數調用另一個函數。

【例4.11】

求兩個整數的最小公倍數。

程序如下:圖4.11例4.11的運行結果例4.11的程序運行過程如圖4.12所示。從main函數開始執行,輸入兩個整數后調用multiple函數,multiple函數用調用了divisor函數。divisor函數執行完返回multiple函數執行,multiple函數執行完返回main函數執行。main函數執行完,程序執行結束。圖4.12嵌套調用圖4.13例4.12的運行結果4.4.2函數的遞歸調用

函數可以直接或間接地調用自身,這種調用方式稱為函數的遞歸調用。例如,例4.13和例4.14都屬于函數的遞歸調用,例4.13直接調用自身,例4.14是間接的調用自身。

注意:雖然遞歸是直接或間接的調用自身,但和調用其他函數一樣每次被調用都需要重新開辟空間,并不共用同一內存空間。因此調用自身和調用其他函數的調用過程沒有

區別。例4.13和例4.14的遞歸調用是無休止的自身調用。顯然無休止的調用是不合理的,也是不允許的。合理的遞歸調用應是有限的,是滿足一定條件能停止的遞歸調用。例4.15就是一個合理的遞歸調用。

【例4.15】

求n!的遞歸程序。

程序如下:圖4.14例4.15的運行結果如圖4.15所示,例4.15在輸入3時的遞歸調用過程為:從main函數調用fac函數求3!開始,變成fac函數調用fac函數求2!,再變成fac函數調用fac函數求1!。然后再返回1!的值,得到2!,返回2!的值,得到3!,返回main函數輸出。圖4.15遞歸調用由例4.15看出,函數遞歸調用的過程可分為遞歸過程和回溯過程兩個階段。

(1)遞歸過程:將原始問題不斷轉化為規模更小且處理方式相同的新問題。

(2)回溯過程:從已知條件出發,沿遞歸的逆過程逐一求值返回,直至遞歸初始處,則完成遞歸調用。

通過fac函數執行過程的分析可以看出遞歸算法解決問題的方式:將初始問題轉化為解決方法相同的新問題,而新問題的規模要比原始問題小;新問題又可以轉化為規模更小的問題;這樣處理問題直至最終歸結到可以簡單解決的問題——遞歸的終結條件。遞歸調用會占用大量內存和時間,使得執行效率低。但采用遞歸方法編寫的程序簡潔、可讀性好。其優點使遞歸方法成為解決某些問題的最佳方法,例如漢諾塔問題。

【例4.16】

漢諾塔問題:古代有一座梵塔,塔內有a、b、c三個底座,上面可放盤子。初始a座上有n個盤子,這些盤子大小各不相同,大盤在下,小盤在上,依次排列。要求將a座上n個盤子移至c座上,每次只能移動一個,但要求移動過程中保持3個座上始終小盤子在上,大盤子在下。移動過程中可以借助b座實現移動。編寫程序求出盤子的移動步驟。

問題分析:這個問題可用遞歸思想分析,將n個盤子由a座移動到c座可分為如下三個過程:

(1)先將a座上n-1個盤子借助c座移至b座;

(2)再將a座上最下面一個盤子移至c座;

(3)最后將b座上n-1個盤子借助a座移至c座。

這樣就把移動n個盤子的問題轉化為移動n-1個盤子的問題。按這種思路,再將移動n-1個盤子的問題轉化為移動n-2個盤子的問題,直至移動一個盤子。程序如下:

圖4.16例4.16的運行結果

4.5變量的作用域

編寫程序時,除了要了解變量占用內存大小和運算規則外,還需要知道變量的作用域和存儲類型。

變量只能在它的作用范圍內使用,即變量在它的作用域之外不能被引用。變量的作用域直接與變量定義的位置相關。在函數內部(或復合語句內部)定義的變量稱為內部變量。內部變量的作用域是定義它的函數(或復合語句)。在函數外任意位置定義的變量稱為外部變量。外部變量的作用域是從定義或聲明的位置開始,直至它所在源程序文件的結束。

【例4.17】

注意變量的作用域。例4.17中main函數的內部變量x、y和函數f1的內部變量x、y雖然同名,卻是完全不相關的變量。因為它們的作用域完全不同,是不同的變量,不產生沖突。如圖4.17所示。

作用域不同的同名變量有時會出現作用域的部分重疊,這時系統通常都選擇作用域范圍小的變量有效。例如例4.18函數f1的內部變量a、b和全局變量a、b重疊,但是執行f1時,有效的是內部變量a、b。圖4.17相關變量的作用域圖4.18例4.18的運行結果內部變量有助于實現信息隱蔽,即使不同的函數定義了同名的內部變量,也不會相互影響。而外部變量的使用增加了函數之間傳遞數據的途徑。如果一個函數需要返回兩個或兩個以上的計算結果時,可以使用外部變量傳遞數據。例如,例4.19使用外部變量將函數f1計算的四個結果傳送到了main函數中。

【例4.19】

求兩個數的和、差、積、商。

程序如下:輸入“8811”時,程序的運行結果如圖4.19所示。

外部變量的使用會降低函數的通用性。過多使用外部變量,會使函數之間的依賴性增加,耦合性增高。這種情況不利于程序的結構化設計方式。因此,除非真正需要,建議避免使用外部變量。圖4.19例4.19的運行結果

4.6變量的存儲類別

根據變量在程序運行期間是否需占用固定的存儲單元,變量的存儲類別可分為以下兩類。

(1)動態存儲:程序運行期間不需要長期占用內存單元。動態存儲的變量有auto(自動)類型和register(寄存器)類型。

(2)靜態存儲:靜態存儲的變量在編譯時被分配空間,在整個程序運行期間一直占用固定的內存空間,程序運行結束才釋放內存空間。可以用static、extern定義和聲明靜態存儲類別的變量。

C程序運行時占用的內存空間通常分為程序區、靜態存儲區和動態存儲區三部分。動態存儲類別的變量可存放在兩個地方——動態存儲區和寄存器。靜態存儲類別的變量只能存放于靜態存儲區中。

定義變量時需要說明存儲類別。因此,完整的變量定義形式應為

存儲類別數據類型變量列表;4.6.1內部變量的存儲類別

內部變量可存放于內存的動態存儲區、靜態存儲區和寄存器。但無論內部變量存放在何處,它的作用域是不變的。

1.auto(自動)變量

C系統默認內部變量的存儲類型是auto類型。例如,函數內部的語句“inta;”和語句“autointa;”是完全等價的。

自動變量在其定義所在的函數(或復合語句)開始執行時才分得內存空間,在該函數(或復合語句)執行期間占用內存空間。在函數(或復合語句)執行結束時自動變量占用的空間被系統收回。

例4.20中的函數f1被調用時,系統在動態存儲區內分配空間給auto變量a、b。當f1結束時,系統回收變量a、b所占內存空間。圖4.20例4.20的運行結果

2.register(寄存器)變量

register變量和auto變量不同之處在于register變量被存放在寄存器中,因此比auto變量存取速度快得多。通常將頻繁使用的變量放在寄存器中,以提高程序的執行速度。例如循環體內涉及的內部變量可定義為register變量。

計算機中寄存器的數量是有限的,而且寄存器的數據長度也是有限的。因此register變量不能定義太多,也不能是數據類型太大的變量(如數組、結構體等類型的變量)。

現今register變量定義通常是不必要的。優化的編譯系統能夠識別使用頻繁的變量,并將其放到寄存器之中。

3.static(靜態)變量

static變量存放在內存中的靜態存儲區。static內部變量在整個程序運行期間占用固定的內存單元。即使static內部變量所在的函數執行結束后,static內部變量也不釋放存儲單元。因而再次調用static內部變量所在函數時,static內部變量的值為上次調用結束時的值。

系統在編譯時為static變量分配空間并賦初值,對于

溫馨提示

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

評論

0/150

提交評論