第5章 函數
5.1 函數定義
在標準C+ +中,函數的定義形式為:
<返回類(lèi)型><函數名>(<形參列表>)
{
<函數體>
。
<函數名>一般是標識符,一個(gè)程序只有一個(gè)main函數,其他函數名可隨意取(當然,必須避免使用C+ +的關(guān)鍵字),好的程序設計風(fēng)格要求函數名最好是取有助于記憶的名字,如getchar函數,通過(guò)函數的名字可以知道函數的功能,這無(wú)疑會(huì )增加程序的可讀性。
<形參列表>是由逗號分隔的,分別說(shuō)明函數的各個(gè)參數。形參將在函數被調用時(shí)從調用函數那里獲得數據。在C+ +中,函數形參列表可以為空,即一個(gè)函數可以沒(méi)有參數。但即使函數形參列表為空,括起函數參數的一對圓括號也不允許省略。
<返回類(lèi)型>又稱(chēng)函數類(lèi)型,表示一個(gè)函數所計算(或運行)的結果值的類(lèi)型。如果一個(gè)函數沒(méi)有結果值,如函數僅用來(lái)更新(或設置)變量值、顯示信息等,則該函數返回類(lèi)型為void類(lèi)型。一個(gè)沒(méi)有返回值的函數類(lèi)似于一些程序語(yǔ)言(如pascal語(yǔ)言)中的過(guò)程(procedure)。
由一對花括號括起來(lái)的<函數體>是語(yǔ)句的序列,它定義了函數應執行的具體操作。
需要注意的是,C+ +不允許函數定義嵌套,即在一個(gè)函數體內不能包含有其他函數的定義。
5.2 函數調用
C+ +中函數調用的一般形式為:
<函數名>(<實(shí)參表>)
當調用一個(gè)函數時(shí),其實(shí)參的個(gè)數、類(lèi)型及排列次序必須與函數定義時(shí)的形參相一致,也就是說(shuō)實(shí)參與形參應該一對一地匹配。當函數定義時(shí)沒(méi)有形參,則函數調用時(shí),<實(shí)參表>亦為空。
依據對函數返回值的使用方式,函數的調用方法可分為以下幾種:
(1)語(yǔ)句調用,這通常用于不帶返回值的函數。這種情況下,被調用函數作為一個(gè)獨立的語(yǔ)句出現在程序中。
(2)表達式調用。將被調用函數作為表達式的一部分來(lái)進(jìn)行調用。它適用于被調用函數帶有返回值的情況。
(3)參數調用。被調用函數作為另一個(gè)函數的一個(gè)參數進(jìn)行調用。
5.3 函數原型
在C+ +中,函數在使用之前要預先聲明。這種聲明在標準C+ +中稱(chēng)為函數原型(function prototype),函數原型給出了函數名、返回類(lèi)型以及在調用函數時(shí)必須提供的參數的個(gè)數和類(lèi)型。函數原型的語(yǔ)法為:
<返回類(lèi)型><函數名>(<形參列表>);
(注意在函數原型后要有分號)
實(shí)際上函數原型說(shuō)明有兩種形式:
(1)直接使用函數定義的頭部,并在后面加上一個(gè)分號。
(2)在函數原型說(shuō)明中省略參數列表中的形參變量名,僅給出函數名、函數類(lèi)型、參數個(gè)數及次序。
注意:在C+ +中,在調用任何函數之前,必須確保它已有原型說(shuō)明。函數原型說(shuō)明通常放在程序文件的頭部,以使得該文件中所有函數都能調用它們。實(shí)際上,標準函數的原型說(shuō)明放在了相應的頭文件中,這也是為什么在調用標準函數時(shí)必須要包含相應的頭文件的原因之一。
在了解了函數定義、函數調用和函數原型之后,就可以寫(xiě)出一個(gè)完整的C+ +程序,并可將其編譯和運行。
5.4 函數返回類(lèi)型
根據函數是否帶有參數以及函數是否有返回值,可以將函數分為如下四類(lèi)。
1帶參數的有返回值函數
定義形式為:
<返回類(lèi)型><函數名>(<參數列表>)
{
<語(yǔ)句序列>
。
2不帶參數的有返回值函數
定義形式為:
<返回類(lèi)型><函數名>()
{
<語(yǔ)句序列>
。
3帶參數的無(wú)返回值函數
定義形式為:
void<函數名>(<參數列表>)
{
<語(yǔ)句序列>
。
4不帶參數的無(wú)返回值函數
定義形式為:
void<函數名>()
{
<語(yǔ)句序列>
。
5.5 函數參數
C+ +中,函數之間傳遞參數有傳值和傳地址兩種傳遞方式。此外,C+ +還提供了默認參數機制,可以簡(jiǎn)化復雜函數的調用。
1參數的傳遞方式
(1)傳值
傳值是將實(shí)參值的副本傳遞(拷貝)給被調用函數的形參。它是C+ +的默認參數傳遞方式,在此之前的多數函數參數傳遞都是傳值。
由于傳值方式是將實(shí)參的值復制到形參中,因此實(shí)參和形參是兩個(gè)不同的變量,有各自的存儲空間,可以把函數形參看作是函數的局部變量。傳值的最大好處是函數調用不會(huì )改變調用函數實(shí)參變量的內容,可避免不必要的副作用。
(2)傳地址
有時(shí)我們確實(shí)需要通過(guò)函數調用來(lái)改變實(shí)參變量的值,或通過(guò)函數調用返回多個(gè)值(return語(yǔ)句只能返回一個(gè)值),這時(shí)僅靠傳值方式是不能達到目的。
2默認參數
在C+ +中,可以為參數指定默認值,在函數調用時(shí)沒(méi)有指定與形參相對應的實(shí)參時(shí)就自動(dòng)使用默認值。默認參數可以簡(jiǎn)化復雜函數的調用。
默認參數通常在函數名第一次出現在程序中的時(shí)候,如在函數原型中,指定默認參數值。指定默認參數的方式從語(yǔ)法上看與變量初始化相似。
5.6 函數重載
如果能用同一個(gè)函數名字在不同類(lèi)型上做相類(lèi)似的操作就會(huì )方便很多,這種情況即為函數重載。其實(shí)這一技術(shù)早已用于C+ +的基本運算符。例如加法操作只有一個(gè)運算符+,但它卻可以用來(lái)做整型數、浮點(diǎn)數和指針的加法運算。將這一思想推廣到函數,即為函數重載。
5.7 內聯(lián)函數
C+ +引入內聯(lián)(inline)函數的原因是用它來(lái)取代C中的預處理宏函數。內聯(lián)函數和宏函數的區別在于,宏函數是由預處理器對宏進(jìn)行替換,而內聯(lián)函數是通過(guò)編譯器來(lái)實(shí)現的,因此內聯(lián)函數是真正的函數,只是在調用的時(shí)候,內聯(lián)函數像宏函數一樣的展開(kāi),所以它沒(méi)有一般函數的參數壓棧和退棧操作,減少了調用開(kāi)銷(xiāo),因此,內聯(lián)函數比普通函數有更高的執行效率。
在C+ +中使用inline關(guān)鍵字來(lái)定義內聯(lián)函數。inline關(guān)鍵字放在函數定義中函數類(lèi)型之前。不過(guò),編譯器會(huì )將在類(lèi)的說(shuō)明部分定義的任何函數都認定為內聯(lián)函數,即使它們沒(méi)有用inline說(shuō)明。
5.8 遞歸函數
如果一個(gè)函數在其函數體內直接或間接地調用了自己,該函數就稱(chēng)為遞歸函數。遞歸是解決某些復雜問(wèn)題的十分有效的方法。遞歸適用以下的一般場(chǎng)合。
(1)數據的定義形式按遞歸定義。
(2)數據之間的關(guān)系(即數據結構)按遞歸定義,如樹(shù)的遍歷,圖的搜索等。
(3)問(wèn)題解法按遞歸算法實(shí)現,例如回溯法等。
使用遞歸需要注意以下幾點(diǎn):
(1)用遞歸編寫(xiě)代碼往往較為簡(jiǎn)潔,但要犧牲一定的效率。因為系統處理遞歸函數時(shí)都是通過(guò)壓棧/退棧的方式實(shí)現的。
(2)無(wú)論哪種遞歸調用,都必須有遞歸出口,即結束遞歸調用的條件。
(3)編寫(xiě)遞歸函數時(shí)需要進(jìn)行遞歸分析,既要保證正確使用了遞歸語(yǔ)句,還要保證完成了相應的操作。
5.9 變量作用域與生存周期
1C+ +中變量的存儲類(lèi)型分為如下幾種類(lèi)型:
auto——函數內部的局部變量(auto可省略不寫(xiě))。
static——靜態(tài)存儲分配,又分為內部和外部靜態(tài)。
extern——全局變量(用于外部變量說(shuō)明)。
register——變量存儲在硬件寄存器中。
(1)自動(dòng)變量
、僭诤瘮祪炔慷x的局部變量即為自動(dòng)變量,用于說(shuō)明自動(dòng)變量的關(guān)鍵字auto可以省略。
、谠诤瘮殿^部定義的自動(dòng)變量作用域為定義它的函數;而在塊語(yǔ)句中定義的自動(dòng)變量作用域為所在塊。與C不同,C+ +還允許在變量使用之前才定義變量。
、劬幾g程序不給自動(dòng)變量賦予隱含的初值,故其初值不確定。因此,每次使用自動(dòng)變量前,必須明確地賦初值。
、苄螀⒖梢钥闯墒呛瘮档淖詣(dòng)變量,作用域僅限于相應函數內。
、葑詣(dòng)變量所使用的存儲空間由程序自動(dòng)地創(chuàng )建和釋放。當函數調用時(shí)為自動(dòng)變量創(chuàng )建存儲空間,函數調用結束時(shí)將自動(dòng)釋放為其創(chuàng )建的存儲空間。因此,自動(dòng)變量隨函數的調用而存在并隨函數調用結束而消失,由一次調用到下一次調用之間不保存值。
(2)外部變量
、僭诤瘮低獠慷x的變量即為外部變量。
、谕獠孔兞康淖饔糜蚴钦麄(gè)程序(全局變量)。
、墼贑+ +中,程序可以分別放在幾個(gè)源文件上,每個(gè)文件可作為一個(gè)編譯單位分別編譯。外部變量只需在某個(gè)文件上定義一次,其他文件若要引用此變量時(shí),應用extern加以說(shuō)明。(外部變量定義時(shí)不必加extern關(guān)鍵字)。
、茉谕晃募,若前面的函數要引用在其后面定義的外部(在函數之外)變量時(shí),也應用extern加以說(shuō)明。
、萃獠孔兞渴怯删幾g程序在編譯時(shí)給其分配空間,屬于靜態(tài)分配變量,對于數值型(整型、浮點(diǎn)型和字符型)外部變量來(lái)說(shuō),其有隱含初值0。
引進(jìn)外部變量的原因:其一是只要程序運行外部變量的值是始終存在的;其二是外部變量可以在所有函數間共享。
在C+ +中,可以使用外部變量,但是,必須要清楚使用外部變量的副作用。使用外部變量的函數獨立性差,通常不能被移植到其他程序中,而且,如果多個(gè)函數都使用到某個(gè)外部變量,一旦出現問(wèn)題,就很難發(fā)現問(wèn)題是由哪個(gè)函數引起的。在C+ +中,盡量不用或少用外部變量,可使用參數在函數間進(jìn)行數據的傳遞。
(3)靜態(tài)變量
內部靜態(tài)變量:
、僭诰植孔兞壳凹由稀皊tatic”關(guān)鍵字就成為內部靜態(tài)變量。
、趦炔快o態(tài)變量仍是局部變量,其作用域仍在定義它的函數內。但該類(lèi)型變量采用靜態(tài)存儲分配,當函數執行完,返回調用點(diǎn)時(shí),該變量并不撤消,其值將繼續保留,若下次再進(jìn)入該函數時(shí),其值仍然存在。內部靜態(tài)變量有隱含初值0,并且只在編譯時(shí)初始化一次。
外部靜態(tài)變量:
、僭诤瘮低獠慷x的變量前加上“static”關(guān)鍵字便成了外部靜態(tài)變量。
、谕獠快o態(tài)變量的作用域為定義它的文件,即成為該文件的“私有”(private)變量,只有其所在文件上的函數可以訪(fǎng)問(wèn)該外部靜態(tài)變量,而其他文件上的函數一律不得直接訪(fǎng)問(wèn)該變量,除非通過(guò)外部靜態(tài)變量所在文件上的各種函數來(lái)對它進(jìn)行操作,這也是一種實(shí)現數據隱藏的方式。
、叟c內部靜態(tài)變量一樣,外部靜態(tài)變量也采用靜態(tài)存儲分配,有隱含初值0。
在C+ +中,除了支持C風(fēng)格的內部和外部靜態(tài)變量的使用之外,還可將類(lèi)成員聲明成static,它有著(zhù)不同的含義。
(4)寄存器變量
、僦挥凶詣(dòng)(局部)變量和函數參數可指定為寄存器存儲類(lèi),它的作用域與生存期與自動(dòng)變量完全相同。
、诋斨付ǖ募拇嫫髯兞總(gè)數超過(guò)系統所能提供的寄存器數量時(shí),多出的寄存器變量將視同自動(dòng)變量。
、壑幌抻趇nt,char,short,unsigned和指針類(lèi)型可使用register存儲類(lèi)。
、懿荒軐拇嫫髯兞咳〉刂(即&操作)。
、菔褂眉拇嫫髯兞靠梢蕴岣叽嫒∷俣,可將使用頻率最高的變量說(shuō)明成為寄存器變量。一般常用于說(shuō)明循環(huán)變量。
由于硬件的快速發(fā)展,存儲器(如內存)的性能有了很大的改進(jìn),因此,目前在實(shí)際應用中,使用register來(lái)說(shuō)明變量的情況并不多。
2生存周期
(1)變量由編譯程序在編譯時(shí)給其分配存儲空間(稱(chēng)為靜態(tài)存儲分配),并在程序執行過(guò)程中始終存在,這類(lèi)變量包括全局變量、外部靜態(tài)變量和內部靜態(tài)變量。這類(lèi)變量的生存周期與程序的運行周期相同,當程序運行時(shí),該變量的生存周期隨即存在,程序運行結束,變量的生存周期隨即終止。
(2)變量由程序在運行時(shí)自動(dòng)給其分配存儲空間(稱(chēng)為自動(dòng)存儲分配),這類(lèi)變量為函數(或塊)中定義的自動(dòng)變量。它們在程序執行到該函數(或塊)時(shí)被創(chuàng )建,在函數(或塊)執行結束時(shí)釋放所用的空間。
北京 | 天津 | 上海 | 江蘇 | 山東 |
安徽 | 浙江 | 江西 | 福建 | 深圳 |
廣東 | 河北 | 湖南 | 廣西 | 河南 |
海南 | 湖北 | 四川 | 重慶 | 云南 |
貴州 | 西藏 | 新疆 | 陜西 | 山西 |
寧夏 | 甘肅 | 青海 | 遼寧 | 吉林 |
黑龍江 | 內蒙古 |