《C++程序設(shè)計語言》課件第6章_第1頁
《C++程序設(shè)計語言》課件第6章_第2頁
《C++程序設(shè)計語言》課件第6章_第3頁
《C++程序設(shè)計語言》課件第6章_第4頁
《C++程序設(shè)計語言》課件第6章_第5頁
已閱讀5頁,還剩109頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

第6章函數(shù)

6.1函數(shù)的聲明6.2函數(shù)的參數(shù)傳遞6.3函數(shù)的返回值6.4函數(shù)名的過載/重載6.5缺省的函數(shù)參數(shù)值6.6遞歸6.7參數(shù)數(shù)目可變的函數(shù)6.8函數(shù)指針6.9綜合示例小結(jié)練習(xí)題本章要點:

函數(shù)的基本概念及定義;

函數(shù)的參數(shù)傳遞規(guī)則;

函數(shù)的返回值;

函數(shù)名的重載;

缺省的函數(shù)參數(shù)值;

遞歸函數(shù);

參數(shù)數(shù)目可變的函數(shù);

函數(shù)指針。函數(shù)(Function)是支持面向過程程序設(shè)計范型最主要的機制。與函數(shù)相關(guān)的主要問題是函數(shù)的參數(shù)傳遞、函數(shù)的返回值和函數(shù)的調(diào)用規(guī)則等。

從函數(shù)的內(nèi)涵講:函數(shù)是具有一定功能的一段代碼的封裝與抽象(Abstract)。在闡述函數(shù)的各種問題細節(jié)之前,讓我們首先思考這樣一個問題:為什么要定義和使用函數(shù)?

如果一段程序代碼的功能是相對完整的,而這段代碼在程序的多個地方都可以用到(即使有一些細節(jié)上的差別),就有理由考慮將這段代碼抽象成一個函數(shù),用不同參數(shù)(實參)來調(diào)用這個函數(shù)以體現(xiàn)上述差別。這樣做的意義不僅在于減少了程序代碼的重復(fù),還在于對程序進行了功能上的劃分,便于代碼的編制、理解和保持一致。

程序設(shè)計語言的發(fā)明與發(fā)展史,實際上亦是人們抽象程度不斷提高、發(fā)展的歷史。計算機發(fā)明的早期,當(dāng)函數(shù)這個概念與機制還沒有誕生時,各種程序即使它們之間存在著許多共性,也幾乎談不到更無法進行任何重用(Reuse)。20世紀(jì)60年代產(chǎn)生的Fortran語言第一次引入了函數(shù)的概念。在程序中引入函數(shù)(有的語言稱為過程或子程序(Procedure))是軟件技術(shù)發(fā)展歷史上最重要的里程碑之一。它標(biāo)志著軟件模塊化與軟件重用的真正開始,F(xiàn)ortran語言也因其貢獻而永遠載入程序設(shè)計語言發(fā)展的史冊!注意兩段程序中的陰影部分,它們的代碼形式雖然不同,但功能卻是一樣的,即完成兩個整型量的交換。完成該功能的代碼在程序中不止一次被用到(雖然存在著細微的差別),為了減少代碼的編寫量,亦為了程序的可重用性等,我們有必要將其抽象為一個函數(shù):從上述實例可看出,定義并實現(xiàn)的swap函數(shù)不僅使代碼更加簡潔,而且swap可被重用于其它需要相同功能的程序中。

6.1函?數(shù)?的?聲?明

一個函數(shù)的首部(函數(shù)體左花括號之前的東西)包括函數(shù)的返回類型、函數(shù)名及函數(shù)的參數(shù)表列,統(tǒng)稱為函數(shù)的接口或界面,它表征了調(diào)用該函數(shù)所需要的信息。函數(shù)聲明分為函數(shù)的(接口/原型)聲明與函數(shù)的定義聲明兩部分。6.1.1函數(shù)接口/原型聲明

在函數(shù)接口的聲明(以下簡稱為函數(shù)聲明)中,必須給出函數(shù)的返回類型(如果有的話)、函數(shù)名,以及調(diào)用這個函數(shù)時所必須提供的參數(shù)的個數(shù)和類型(常稱為參數(shù)表列)。

注意:在函數(shù)聲明中,可不給出函數(shù)參數(shù)(常稱為形參,F(xiàn)ormalArguments)的具體名字,因為編譯器會忽略掉它,但為了程序的可讀性,建議在函數(shù)聲明時最好給出各參數(shù)確切的、有意義的參數(shù)名。例如下面一些語句:

Elem*next_elem();

char*strcpy(char*to,constchar*from);

voidexit(int);

都是函數(shù)聲明語句。6.1.2函數(shù)的定義

一個函數(shù)調(diào)用就是跳轉(zhuǎn)到函數(shù)的代碼處而執(zhí)行之。因此,在程序中調(diào)用的每個函數(shù)都必須在某個地方定義(且僅能定義一次)。一個函數(shù)定義即給出了函數(shù)體(以一對花括號為界)的函數(shù)聲明。例如:由于C++對任何用戶標(biāo)識符(函數(shù)名亦屬于用戶標(biāo)識符)采用“先使用,后核實”的原則,故一個函數(shù)若使用在前,定義在后,則在調(diào)用該函數(shù)前,必須對此函數(shù)進行(接口/原型)聲明,且其聲明必須與它所引用的函數(shù)定義的接口/原型完全相同(形參名可不同)。

為了提高函數(shù)調(diào)用的效率,C++引入了另一種函數(shù)定義,即內(nèi)聯(lián)函數(shù)(inline)。inline是一個信號,它告訴編譯器在編譯時將對該在線函數(shù)的調(diào)用“在線化”,即將該在線函數(shù)的調(diào)用語句自動置換為該在線函數(shù)代碼。因此,在執(zhí)行函數(shù)調(diào)用時,就免去了函數(shù)調(diào)用中跳轉(zhuǎn)、壓棧與后續(xù)的彈棧動作,從而提高了函數(shù)調(diào)用的效率。注意:inline描述符并不影響函數(shù)本身的語義。若需定義一個在線函數(shù),僅需在函數(shù)定義中的函數(shù)返回類型前加關(guān)鍵字inline即可。例如:

inlineintfac(intn)

{

return(n<2)?1:n*fac(n-1);

}

即定義了一個在線函數(shù)。

6.2函數(shù)的參數(shù)傳遞

用函數(shù)調(diào)用所給出的實參(實際參數(shù),ActualArgument)向函數(shù)定義給出的形參(形式參數(shù),F(xiàn)ormalArgument)設(shè)置初始值的過程稱為參數(shù)傳遞(ArgumentPassing)。

函數(shù)調(diào)用時,形參被分配空間,并以相應(yīng)的實參對形參進行初始化(此過程稱為虛實結(jié)合或參數(shù)傳遞)。(注意:形參是一個局部于該函數(shù)的局部變量,在程序線程執(zhí)行到此函數(shù)調(diào)用時,形參才被分配空間。)實參向形參傳遞的語義與變量/常量初始化的語義相同。在虛實結(jié)合過程中,編譯器將對實參和形參在個數(shù)、次序及類型上做相對應(yīng)的匹配檢查,并進行必要的隱式類型轉(zhuǎn)換。例如:根據(jù)形參類型的類別,參數(shù)傳遞可分為以下兩類:值調(diào)用(CallbyValue)和引用調(diào)用(CallbyReference)。

所謂值調(diào)用,即實參向形參傳遞的是實參的值;而對于引用調(diào)用,實參向形參傳遞的是實參的引用。在C++?中,除了定義成引用類型的形參外,其它類型的形參都對應(yīng)著值

調(diào)用。

在函數(shù)內(nèi)對值調(diào)用的形參的值所進行的修改,不會影響調(diào)用方的實參變量;函數(shù)中對引用調(diào)用的形參的值所進行的修改,會影響調(diào)用方的實參變量。例如:當(dāng)形參的類型為數(shù)組類型,虛實結(jié)合時,數(shù)組的首地址將被傳遞給形參,即此時參數(shù)的類型將從T[]類型轉(zhuǎn)換為T*類型。例如:

intstrlen(constchar*p); //C標(biāo)準(zhǔn)庫函數(shù)strlen的原型聲明

voidsort(inta[],intlength); //用戶自定義的排序函數(shù)原型聲明

voidf()

{

charv[]=“anarray”;

inti=strlen(v);

//虛實結(jié)合等價于constchar*p=(char*)&v[0];

intj=strlen(“Nicholas”);

/*虛實結(jié)合等價于

constchar*temp=“Nicholas”;

constchar*p=(char*)&temp[0];

*/

sort(v,strlen(v));//等價于a=v,數(shù)組的整體賦初值

}

若在調(diào)用函數(shù)時,不允許在函數(shù)體內(nèi)修改形參的值,如上例所示,應(yīng)將形參以const約束。

6.3函數(shù)的返回值

當(dāng)一個函數(shù)聲明其返回類型為非void時,函數(shù)應(yīng)返回一個值;相反,當(dāng)返回類型聲明為void類型時,函數(shù)應(yīng)不返回值。

函數(shù)的返回值由函數(shù)體中的return語句實現(xiàn)。在一個函數(shù)體內(nèi)可有多個return語句,但每一次函數(shù)調(diào)用應(yīng)保證只有一個return語句被執(zhí)行。利用“return;”語句亦可實現(xiàn)函數(shù)不返回值的功能。函數(shù)返回的語義和參數(shù)傳遞的語義類似,它等同于以return語句中的表達式向一個未命名的(匿名的)、一個函數(shù)返回類型的變量賦初值。在函數(shù)返回時會檢查返回語句表達式的類型與函數(shù)的返回類型是否匹配,并自動進行兩者必要的類型轉(zhuǎn)換。

正如前面曾提到的那樣:形參和函數(shù)內(nèi)定義的變量均屬于局部變量或自動(Automatic)變量。它們的作用域為定義于它的函數(shù),變量的生命期(局部的static變量除外)為此函數(shù)的一次調(diào)用。因此,一個指向局部變量的指針或引用永遠亦不應(yīng)該從函數(shù)中返回,盡管C++的語法對此并無限制。例如:上述代碼中的兩個return語句雖無語法錯誤,但語義是錯誤的。由于local是一個局部變量,當(dāng)函數(shù)體執(zhí)行完時,系統(tǒng)釋放其所分配的內(nèi)存空間,因此,將無法返回其指針或引用。正確的做法是:將local定義為一個static類型的局部變量。由于一個局部的static類型的變量其生命期為程序的一次運行,作用域為定義于它的塊或函數(shù),故當(dāng)函數(shù)體執(zhí)行完時,它仍然存在,所以可返回其地址或引用。

在一個返回值類型為void的函數(shù)中,可無return語句,或出現(xiàn)沒有返回值的return語句,或出現(xiàn)調(diào)用另一個返回值類型為void函數(shù)的return語句。例如:

6.4函數(shù)名的過載/重載

6.4.1函數(shù)名過載/重載的基本概念

通常,我們應(yīng)該為不同的函數(shù)起不同的函數(shù)名,以明確表示它們完成的功能。但當(dāng)某些函數(shù)概念上是完成同樣的任務(wù)(即具有相同的功能),只是其操作對象(指形參)的類型不同時,那么,為這些函數(shù)起一個共同的名字可能更方便于使用。

用同一個名字表示對不同類型對象的操作被稱為(名字)重載/過載(Overloading)。實際上,名字重載的概念我們早已接觸過。例如,符號(Notation)“+”根據(jù)其所處的上下文,既可以表示兩個整型量加,亦可以表示兩個浮點型量的加或其它類型量的加,等等。一組具有重載/過載的函數(shù)名(OverloadedFunctionNames)的函數(shù)(簡稱過載/重載函數(shù)),是指它們在同一區(qū)域內(nèi)有相同的函數(shù)名、不同的參數(shù)表(參數(shù)數(shù)目不同,或類型不同,或順序不同)。但是,僅僅是返回值類型不同的兩個過載/重載函數(shù),編譯程序不按過載/重載函數(shù)對待,而按重復(fù)定義了兩個函數(shù)進行報錯處理。例如,我們可定義如下過載函數(shù):

int

add(intx,inty);

floatadd(floatx,floaty);

doubleadd(doublex,doubley);6.4.2重載函數(shù)的匹配規(guī)則

如果程序中出現(xiàn)了一組重載的函數(shù),則編譯器在進行靜態(tài)綁定時,根據(jù)重載函數(shù)參數(shù)表的內(nèi)容進行唯一的匹配綁定(即靜態(tài)確定函數(shù)調(diào)用與函數(shù)定義代碼的匹配)。

重載函數(shù)靜態(tài)綁定時,按以下原則進行:

(1)準(zhǔn)確匹配,即形參和實參個數(shù)、次序相同,類型無須任何轉(zhuǎn)換或者只需做平凡轉(zhuǎn)換(例如數(shù)組[]到char*、函數(shù)名到函數(shù)指針、T到constT等)的匹配。

(2)提升的匹配,即形參和實參個數(shù)、次序相同,但類型需要提升轉(zhuǎn)換(如bool到int、short到int以及short到unsignedint、float到double等)。

(3)利用標(biāo)準(zhǔn)轉(zhuǎn)換的匹配,例如int到double、double到int、double到longdouble、子類到父類(詳見第二部分的內(nèi)容)、T*到void*、int到unsignedint等。

(4)用戶自定義的類型轉(zhuǎn)換,詳見第二部分的內(nèi)容。

(5)函數(shù)聲明中的省略號(...)的匹配。

例如對一組重載的函數(shù):

voidprint(int);

voidprint(constchar*);

voidprint(double);

voidprint(long);

voidprint(char);

voidh(charc,inti,shorts,floatf)

{

print(c); //準(zhǔn)確匹配,調(diào)用print(char)

print(i); //準(zhǔn)確匹配,調(diào)用print(int)

print(s); //提升匹配,調(diào)用print(int)

print(f); //提升匹配,調(diào)用print(double)

print(‘a(chǎn)’); //準(zhǔn)確匹配,調(diào)用print(char)

print(“student”); //準(zhǔn)確匹配,調(diào)用print(constchar*)

}在函數(shù)調(diào)用的匹配時,若出現(xiàn)了兩個或兩個以上可能的匹配,則該函數(shù)調(diào)用被認(rèn)為存在歧義性而遭拒絕,此時編譯器報錯。例如:

voidprint(float);

voidprint(double);

voidprint(long);

voidf()

{

print(1L); //正確!

匹配print(long)

print(1.0); //正確!

匹配print(double)

print(1); //存在歧義性!?上述三個print函數(shù)都可匹配

}對上述問題可采用如下方法進行解決。

(1)在上述一組重載函數(shù)的基礎(chǔ)上,再加入一個inline重載的函數(shù)f,其定義如下:

inlinevoidf2(intn)

{

f1(long(n));

} //此時f2(0)調(diào)用將準(zhǔn)確匹配f2(intn)

(2)將f2(0)函數(shù)調(diào)用改為

f2(static_cast<int*>(0));//此時f2(0)調(diào)用將準(zhǔn)確匹配f2(int*)當(dāng)然,上述辦法只是一種就事論事的權(quán)宜之計。欲真正避免重載函數(shù)的歧義性,編程時,我們應(yīng)該將函數(shù)的重載版本集合作為一個整體來考慮,查看對于函數(shù)的語義而言它們是否有意義,從而通過增加或刪除重載的函數(shù)版本來消除程序中函數(shù)重載匹配所產(chǎn)生的歧義性問題。

如果一組重載的函數(shù)具有多個參數(shù),函數(shù)調(diào)用與之匹配時,仍然根據(jù)上述匹配規(guī)則進行參數(shù)的最佳匹配。不過這種情況較之只有一個參數(shù)的重載函數(shù)的匹配而言,將變得稍復(fù)雜一些。對一個函數(shù)調(diào)用,如果某個參數(shù)具有最佳匹配,其它參數(shù)的匹配通過提升匹配等又優(yōu)于其它函數(shù)的匹配時,這個函數(shù)將被調(diào)用;若通過匹配查找,在一組重載的函數(shù)中沒有這樣的函數(shù),則此函數(shù)調(diào)用被認(rèn)為具有歧義性而遭拒絕。例如:

//一組重載的pow函數(shù)

intpow(int,int);

doublepow(double,double);

complexpow(double,complex); //complex為一用戶自定義類型

complexpow(complex,int);

complexpow(complex,double);

complexpow(complex,complex);

voidk(complexz)6.4.3重載函數(shù)與函數(shù)的返回類型

在重載函數(shù)的解析(指函數(shù)調(diào)用與重載函數(shù)的綁定)時,忽略函數(shù)的返回類型(即函數(shù)的返回類型不被考慮),其主要原因是C++欲保持對重載的解析只針對單獨的運算符或函數(shù)調(diào)用,而與它們所處的上下文環(huán)境無關(guān)。例如:上述代碼片斷中,若對sqrt函數(shù)調(diào)用的解析將重載函數(shù)的返回類型亦考慮進去,將使得情況變得更加復(fù)雜,編譯器和運行環(huán)境會感到無所適從。6.4.4重載與作用域

C++規(guī)定:重載函數(shù)的作用域只在其“名字空間”內(nèi),不同“名字空間”中同名的函數(shù)不是重載函數(shù)。上述的“名字空間”,其區(qū)域范圍是比名字空間namespace更一般或?qū)挿阂饬x上的區(qū)間,如不同的名字空間,不同的類、類與函數(shù)、函數(shù)與函數(shù)以及函數(shù)與全局區(qū)間,等等。例如,下面代碼中的兩個f由于處于不同的“名字空間”,所以它們不是重載函數(shù):

6.5缺省的函數(shù)參數(shù)值

通常,一個通用函數(shù)(指接口尺寸較寬,可適用于各種不同參數(shù)的組合處理)所需要的參數(shù)要比一個普通函數(shù)(指接口尺寸較窄,只適合于特定參數(shù)的處理)所需要的參數(shù)要多,例如通用的對象構(gòu)造函數(shù)(Constructor,詳見第二部分內(nèi)容)等。編寫通用函數(shù)的目的是用更高的抽象進一步表示函數(shù)概念,為用戶提供更為通用、靈活的函數(shù)調(diào)用方式。

C++允許在定義函數(shù)時聲明一些具有缺省值的形參(DefaultArguments)。如果在調(diào)用時有對應(yīng)的實參,就與普通的參數(shù)傳遞相同;如果沒有給出對應(yīng)的實參,則將缺省值作為實參傳入。

C++語法規(guī)定:

(1)一個函數(shù)參數(shù)的缺省值只能位于函數(shù)參數(shù)表的尾部,即缺省值的形參必須位于參數(shù)表的后部,而且中間沒有插入非缺省值的形參。

(2)一個具有缺省值的形參在其作用域內(nèi)不能被重復(fù)定義,或者,一經(jīng)定義后,也不能在后續(xù)的代碼中對其缺省值進行修改。

我們以下述代碼對上述規(guī)則做進一步的詮釋。

intf(int,int=0,char*=0); //正確!

intg(int=0,int=0,char*); //錯誤!缺省值未在尾部

inth(int=0,int,char*=0); //錯誤!中間有非缺省值注意:一般而言,函數(shù)重載與缺省的參數(shù)值這兩種機制不宜同時使用,若兩者同時使用極易造成二義性(歧義性)。例如,當(dāng)定義了如下重載函數(shù)時:

intprint(intx);

//重載函數(shù)

intprint(intvalue,intbase=10);

//重載函數(shù),并使用缺省的參數(shù)值則調(diào)用

print(230);//具有歧義性!調(diào)用print(int)或print(int,int)?

6.6遞歸

6.6.1遞歸的基本概念

有了函數(shù)抽象機制后,大多數(shù)問題我們可采用嚴(yán)格的、以層次化方式調(diào)用函數(shù)的形式構(gòu)造程序(假定程序是按過程化程序設(shè)計范型設(shè)計的)。但某些問題,卻只能用或方便于函數(shù)自己調(diào)用自己或通過另一個函數(shù)間接調(diào)用自己的方式解決。

一個函數(shù)在它的定義中出現(xiàn)了直接或間接地調(diào)用自己,這樣的函數(shù)稱為遞歸函數(shù)(RecursiveFunction)。

采用遞歸函數(shù)解決問題時,函數(shù)將問題分為兩個概念性部分:

(1)函數(shù)能夠處理的部分,稱為基本情況;

(2)函數(shù)不能處理的部分,稱為遞歸情況。

對基本情況的處理,函數(shù)調(diào)用只是簡單地返回一個結(jié)果;對不能處理的部分,函數(shù)模擬原問題,將問題簡化或縮小,啟動自己調(diào)用自己的最新副本來處理這個較小的問題(原函數(shù)調(diào)用壓棧),該過程稱為遞歸調(diào)用(RecursiveCall)或遞歸步驟(RecursiveStep)。遞歸步驟可能導(dǎo)致更多的遞歸調(diào)用,每一次遞歸都是將問題一步步地簡化縮小,直至遞歸停止(即達到基本情況),此時,函數(shù)調(diào)用識別并處理這個基本情況,并向前一個函數(shù)調(diào)用(彈出上一層的函數(shù)調(diào)用)返回結(jié)果。以此類推,回溯一系列結(jié)果,直至(棧中)最頂層的函數(shù)調(diào)用能夠計算出結(jié)果為止。采用遞歸計算4!的過程如圖6.1所示。圖6.1采用遞歸計算4!的過程示意圖6.6.2遞歸的定義及遞歸函數(shù)的編寫模式

遞歸定義在形式上由兩部分組成:

(1)遞歸的基本情況,也就是可使遞歸調(diào)用終止的條件,因此不致出現(xiàn)無限遞歸。

(2)定義中直接或間接地調(diào)用本函數(shù)(函數(shù)模擬原問題,以簡化或縮小原問題)。

我們以計算n!為例,上述兩部分分別為

1!=1 遞歸的基本情況

n!=n*(n-1)! 自己直接調(diào)用自己(以簡化和縮小的方式模擬原問題)遞歸函數(shù)的編寫有固定的模式。我們以直接遞歸為例,其程序框架為

if(基本情況)

{

return基本情況值;

}

else

{

return(模擬原問題的)遞歸調(diào)用;

}

C和C++都是支持遞歸調(diào)用的語言(某些語言不支持遞歸,如Fortran語言等)。應(yīng)用中很多問題采用遞歸的方式不僅表達得更自然,而且程序員編寫程序的復(fù)雜性會降低,所編的程序更易于理解和修改。例如著名的漢諾塔問題、斐波納契(Fibonacci)數(shù)列等問題,采用遞歸方式將明顯大大優(yōu)于非遞歸方式。

遞歸的主要問題是機器的時空開銷較大(多層的壓棧與彈棧),但在今天,較程序員的效率和程序的可讀性等方面而言此問題已可忽略。

下面我們舉例說明遞歸函數(shù)的編寫方法。

5!=120

6!=720

7!=5040

8!=40320

9!=362880

10!=3628800

例2使用遞歸計算Fibonacci(斐波納契數(shù)列)0,1,1,2,3,5,8,13,21,…的第10項(計算斐波納契數(shù)列第n項的遞歸計算公式為f(n)=f(n-1)+f(n-2))。

6.7參數(shù)數(shù)目可變的函數(shù)

編寫通用函數(shù)的另一重要手段是利用C++提供的可定義參數(shù)數(shù)目可變的函數(shù)這一機制。

對某些函數(shù)而言,當(dāng)它無法確定每次函數(shù)調(diào)用時所需的參數(shù)的個數(shù)及其相應(yīng)的類型時,這樣的函數(shù)就可定義聲明成形參表列具有以省略號(Ellipsis…)結(jié)尾形式的函數(shù)。省略號的含義為調(diào)用時“可能需要更多的參數(shù)”。

這類函數(shù)最典型的代表就是C標(biāo)準(zhǔn)庫函數(shù)printf,請看其接口聲明:

intprintf(constchar*,…);

因此,下面都是printf的合法調(diào)用:

printf(“Hello,world!\n”);//一個參數(shù)

printf(“sum(%d,%d)=%d\n”,i,j,i+j);

//四個參數(shù)

在具有四個參數(shù)的printf函數(shù)中,第一個參數(shù)為格式串,其中所包含的一些特殊字符稱為格式描述符,如%d、%s等。格式串中的格式描述符使得printf能正確地拾取格式串之后的參數(shù)個數(shù)及其相應(yīng)的類型并處理之。%s表示“期待一個char*型參數(shù)”,%d表示“期待一個int型參數(shù)”,%f表示“期待一個float型參數(shù)”,等等。對printf而言,格式串中的格式描述符可認(rèn)為是printf中的形參表列,格式串之后的參數(shù)序列可認(rèn)為是對應(yīng)的實參表列。執(zhí)行printf調(diào)用所完成的動作是:根據(jù)形參表列參數(shù)的個數(shù)和類型依次進行相應(yīng)的虛實結(jié)合并打印。

因此,上述兩個printf的調(diào)用輸出為

“Hello,world!”(回車)

“sum(2,6)=8”(回車) //假定i=2,j=6

C++規(guī)定:

(1)參數(shù)數(shù)目可變的函數(shù)至少應(yīng)當(dāng)有一個確定的形參(char*),而不能是

Tf(…);上述程序模式中的va_list、va_start和va_end都是<cstdarg>頭文件中定義的宏(關(guān)于宏的概念詳見C語言的相關(guān)內(nèi)容)。

下面我們用一實例向讀者展示如何編寫參數(shù)數(shù)目可變的函數(shù)。

例3:編寫具有printf形式的錯誤處理函數(shù)。

#include<iostream>

#include<cstdlib>

#include<cstdarg>

#include<cstring>

#include<conio.h>程序運行輸出結(jié)果:

test 4

String 6

Student 7

2006year 9

編寫參數(shù)數(shù)目可變的函數(shù)時,應(yīng)注意以下兩點:

(1)對于參數(shù)數(shù)目可變的函數(shù)而言,由于編譯器在編譯時無法知道其形參與實參的確切信息(這些信息只有在運行時才能知道),因此,下面的代碼語法上是合法的:

printf(“Mynameis%s%s”,2); //實參個數(shù)和類型均與形參不吻合

(2)一個設(shè)計良好的程序,只有在極少的情況下才使用這種參數(shù)個數(shù)、類型未完全刻畫好的函數(shù)(針對編譯器而言)。因此,參數(shù)數(shù)目可變的函數(shù)是一把“雙刃劍”,用不好程序?qū)⒆允芷浜Α?/p>

6.8函數(shù)指針

函數(shù)指針的概念、定義及基本使用方法我們在第5章作了簡要介紹。函數(shù)指針與函數(shù)有著密不可分的關(guān)系,因此我們進一步討論函數(shù)指針的相關(guān)問題。

函數(shù)名即為函數(shù)的指針(即存放函數(shù)代碼的首地址)。函數(shù)指針變量可用以存放某類函數(shù)的地址。

對一個函數(shù)而言,我們對它只能施加兩種操作:取地址和調(diào)用。

程序運行過程中,每個函數(shù)的可執(zhí)行代碼都被存儲在指定的、地址不同的存儲區(qū)(代碼段)中。C++允許用下面的操作取出指定函數(shù)的起始地址(稱為入口地址):

&函數(shù)名或函數(shù)名

如果將函數(shù)入口地址取出后,賦給對應(yīng)類型(函數(shù)指針類型)的變量,那么這樣的變量就稱為函數(shù)指針變量。

通過函數(shù)指針只能調(diào)用函數(shù)指針?biāo)付ǖ奶囟愋偷暮瘮?shù)。

函數(shù)指針的用途主要是支持在不需要(或不能夠)知道函數(shù)名稱的情況下,以間接調(diào)用的方式來調(diào)用函數(shù)。例如,Windows對用戶命令的處理函數(shù)就是以函數(shù)指針的方式進行的(Windows響應(yīng)用戶命令時,并不知道其相應(yīng)的命令處理函數(shù)名,只知道其函數(shù)名與菜單項的相對偏移量)。采用函數(shù)指針fp調(diào)用函數(shù)時,其調(diào)用形式為

(*fp)(實參表列) //fp為指向函數(shù)的指針

或fp(實參表列)

Windows對用戶命令(選擇菜單項目)進行響應(yīng)的示意代碼如下:

typedefint(*FP)(char*); //函數(shù)指針類型FP

//對應(yīng)菜單項目的各命令處理函數(shù)

intf(char*p);

intg(char*p);

inth(char*p);采用函數(shù)指針變量的實質(zhì)就是用數(shù)據(jù)表示控制(即利用函數(shù)指針(數(shù)據(jù)),使得每一次函數(shù)調(diào)用具有不同的結(jié)果),以供程序員編寫通用的處理程序。本章6.9節(jié)將給出解決第5章求若干積分問題的程序代碼。

6.9綜合示例

本節(jié)我們給出一些綜合示例,以說明本章所講述的一些基本概念及相應(yīng)機制的使用方法。

例4

編寫一函數(shù),該函數(shù)能將一整數(shù)分別以二進制、八進制、十進制和十六進制的形式輸出(注:二進制以原碼的形式輸出)。

#include<iostream>

usingnamespacestd;

constintMAX=255;

voidprint(constintvalue,intbase=10)

例6

編寫一通用積分計算(采用梯形法)函數(shù),使之能計算如下函數(shù)的積分:

#include<iostream>

usingnamespacestd;

//積分計算函數(shù),a、b為積分上、下限,n為積分計算步長,pf為被積函數(shù)指針

doubleintegral(doublea,doubleb,intn,double(*pf)(doublex))

{

doubled,s;函數(shù)是支持面向過程程序設(shè)計最重要的機制。下面我們給出一小型應(yīng)用程序的分析、設(shè)計與實現(xiàn)過程,以此進一步向讀者闡述和展示面向過程的程序設(shè)計方法。

例7

一個簡單桌面計算器的設(shè)計與實現(xiàn)。

說明:

(1)小型桌面計算器的功能。桌面計算器實際上是一個小型的某種語言(語言的文法描述見下述內(nèi)容)的編譯器,它能完成:

①從標(biāo)準(zhǔn)輸入設(shè)備讀入一個(數(shù)值計算)表達式,計算它的值后從標(biāo)準(zhǔn)輸出設(shè)備輸出;讀入的可以是一個賦值語句:左端是一個符號名,右端是表達式。②表達式中可以有四則運算符、括號、整數(shù)/實數(shù)值、已經(jīng)賦值的符號名和預(yù)定義的符號常量(pi和e),也可以只有單個的整數(shù)/實數(shù)值。

③若發(fā)現(xiàn)輸入內(nèi)容與文法不符或?qū)?dǎo)致非法計算,則從標(biāo)準(zhǔn)輸出設(shè)備輸出出錯提示,并計算出錯次數(shù)。

(2)程序中關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)。

enumToken_value{?NAME,NUMBER,END,PLUS=‘+’,MINUS=‘-’,MUL=‘*’,

DIV='/',PRINT=';',ASSIGN='=',LP='(',RP=')',};

其中,Token_value為枚舉類型,枚舉了該語言中的各種終結(jié)符標(biāo)記(token)值。

(3)語言的文法。語言的文法以遞歸的方式進行定義,其中終結(jié)符(TerminalSymbols)以黑色粗體/大寫字符標(biāo)識。①?curr_tok:在get_token中設(shè)置,在expr、term、prim中的switch中使用。它表示的是當(dāng)前讀入的標(biāo)記的類別,用來控制分類別的求值及其他處理。

②?number_value:在get_token中設(shè)置,在prim中使用。它表示的是當(dāng)前讀入的數(shù)值字面值。

③?string_value:在get_token中設(shè)置,在prim中使用。它表示的是當(dāng)前讀入的符號名,用來在table中查找對應(yīng)的數(shù)值。

④?table:在prim中設(shè)置,在prim中使用。它表示的是已經(jīng)讀入的符號名與對應(yīng)數(shù)值,符號名可以增加,對應(yīng)數(shù)值通過引用類型隱含地賦值。⑤?no_of_errors:在error中設(shè)置,在main中使

溫馨提示

  • 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論