《C語(yǔ)言程序設(shè)計(jì)》課件2第8章_第1頁(yè)
《C語(yǔ)言程序設(shè)計(jì)》課件2第8章_第2頁(yè)
《C語(yǔ)言程序設(shè)計(jì)》課件2第8章_第3頁(yè)
《C語(yǔ)言程序設(shè)計(jì)》課件2第8章_第4頁(yè)
《C語(yǔ)言程序設(shè)計(jì)》課件2第8章_第5頁(yè)
已閱讀5頁(yè),還剩192頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第8章函數(shù)8.1函數(shù)概念8.2函數(shù)的參數(shù)和返回值8.3函數(shù)間的參數(shù)傳遞8.4函數(shù)的嵌套與遞歸調(diào)用8.5*函數(shù)與指針8.6main()函數(shù)的參數(shù)和返回值8.7文件包含與條件編譯8.8*C程序項(xiàng)目設(shè)計(jì)8.9模塊化程序設(shè)計(jì)舉例習(xí)題8

本章學(xué)習(xí)要求:

1.了解函數(shù)的定義,掌握函數(shù)的調(diào)用(一般調(diào)用、嵌套調(diào)用、遞歸調(diào)用),了解動(dòng)態(tài)存儲(chǔ)與靜態(tài)存儲(chǔ)的區(qū)別。

2.理解內(nèi)部函數(shù)與外部函數(shù)的概念,理解形式參數(shù)、實(shí)際參數(shù)、局部變量、全局變量的概念。

3.了解條件編譯和C程序項(xiàng)目設(shè)計(jì),了解函數(shù)指針變量的概念及其使用。

4.掌握return語(yǔ)句的使用,掌握參數(shù)的傳遞方式(值傳遞、地址傳遞),掌握變量作用域,掌握靜態(tài)變量的使用,掌握函數(shù)嵌套調(diào)用和遞歸調(diào)用,掌握文件包含。

構(gòu)成C語(yǔ)言程序的基本單位是函數(shù)。函數(shù)也是C程序中模塊化程序設(shè)計(jì)的基礎(chǔ)。C函數(shù)可分為標(biāo)準(zhǔn)庫(kù)函數(shù)和用戶定義函數(shù)兩類。前者是系統(tǒng)定義的,它們的定義分別存在不同的頭文件中,用戶只要用

#include文件將頭文件包含到程序中即可調(diào)用它們;后者則是用戶為解決自己的特定問(wèn)題而自行編寫(xiě)的。本章主要介紹用戶定義函數(shù)的設(shè)計(jì)和調(diào)用問(wèn)題。當(dāng)然,在自行設(shè)計(jì)程序時(shí),充分利用系統(tǒng)提供的庫(kù)函數(shù),可以大大減輕程序設(shè)計(jì)的負(fù)擔(dān)。

一個(gè)較大的程序一般應(yīng)分為若干個(gè)程序模塊,每個(gè)模塊用于實(shí)現(xiàn)一個(gè)特定的功能。一個(gè)C程序由一個(gè)主函數(shù)(main)和若干個(gè)其他函數(shù)(0個(gè)到多個(gè))構(gòu)成。程序的執(zhí)行總是從主函數(shù)開(kāi)始,到主函數(shù)結(jié)束。同一個(gè)函數(shù)可以被一個(gè)或多個(gè)函數(shù)調(diào)用任意多次。如圖1.9中“子模塊5”就被調(diào)用兩次。下面舉例予以說(shuō)明。8.1函數(shù)概念

例8.1

從鍵盤(pán)輸入兩個(gè)正整數(shù)m與n(m大于n),求m!/(m-n)!的值(即求)。

程序如下:

#include<stdio.h>

main() /*主函數(shù)*/

{intm,n;

longjiec(); /*聲明函數(shù),說(shuō)明本函數(shù)中要調(diào)用的函數(shù)jiec(),

在本函數(shù)后面定義*/

printf("Pleaseinputm,n(m>n):");

scanf("%d,%d",&m,&n);

while(m<=n||n<=0||m<=0)/*本循環(huán)用來(lái)保證輸入的m大于 n,并且m,n都是正整數(shù)*/

{printf("\nPleaserepeatinputm,n(m>n):");scanf("%d,%d",&m,&n);

}

printf("\nm!/(m-n)!=%ld\n",jiec(m)/jiec(m-n));

}

longjiec(intk) /*計(jì)算階乘值的函數(shù),函數(shù)名前的int 表示返回值的類型*/

{ints,i;

s=1;

for(i=1;i<=k;i++)s*=i; /*計(jì)算1*2*3*…*k,并將計(jì)算結(jié)果賦 值給變量s*/

return(s); /*將計(jì)算得到的階乘值返回調(diào)用函數(shù) (這里是主函數(shù))*/

}

上面程序中,一共有兩個(gè)函數(shù):一個(gè)是主函數(shù)main(),完成的功能是從鍵盤(pán)輸入兩個(gè)正整數(shù)m與n,通過(guò)調(diào)用函數(shù)jiec()計(jì)算并輸出jiec(m)/jiec(m-n)的值;另一個(gè)函數(shù)是jiec(),它完成的功能是計(jì)算階乘值,它通過(guò)從主函數(shù)得到一個(gè)參數(shù)k,計(jì)算k!,并將計(jì)算結(jié)果返回調(diào)用函數(shù)(主函數(shù))。下面作幾點(diǎn)說(shuō)明:

(1)一個(gè)C程序可以由若干個(gè)函數(shù)組成,其中必須有且僅有一個(gè)主函數(shù)main()。C程序總是從主函數(shù)開(kāi)始執(zhí)行(不管它在程序中的什么位置),而其他函數(shù)只能被調(diào)用。

(2)一個(gè)C程序可以由一個(gè)或多個(gè)源程序文件組成。C程序的編譯以源程序文件為編譯單位,而不是以函數(shù)為單位進(jìn)行編譯的。被調(diào)用函數(shù)與調(diào)用函數(shù)可以分別放在不同的源程序文件中,可以分別編寫(xiě)、分別編譯,但最后必須連接成一個(gè)程序進(jìn)行運(yùn)行。

(3)在C語(yǔ)言中,所有函數(shù)都是平行的,即在定義函數(shù)時(shí)是互相獨(dú)立的,互不從屬。即C函數(shù)不能嵌套定義。

(4)程序中的

#include是將要用到的庫(kù)函數(shù)的頭文件包含到程序中來(lái),這里因?yàn)橛玫搅藰?biāo)準(zhǔn)輸入/輸出函數(shù)scanf()和printf(),才使用

#include<stdio.h>。

(5)如果被調(diào)用函數(shù)在調(diào)用函數(shù)的后面定義,則必須在調(diào)用函數(shù)中進(jìn)行聲明。函數(shù)聲明的一般形式如下:

類型標(biāo)識(shí)符函數(shù)名([形參表列]);

其中“形參表列”是可以省略的。最簡(jiǎn)單聲明函數(shù)的方式,就是將函數(shù)定義時(shí)的頭部(在括號(hào)中含形參類型說(shuō)明)作為聲明的表達(dá)式,加上分號(hào)就構(gòu)成了函數(shù)的聲明語(yǔ)句。例如,例8.1中函數(shù)的聲明就是將其省略,該聲明可以寫(xiě)成以下兩種形式:

longjiec(int);

或 longjiec(intk);

1.函數(shù)的定義

在使用一個(gè)函數(shù)前,需要先對(duì)其進(jìn)行定義。

函數(shù)定義通常由兩部分組成:函數(shù)頭部和函數(shù)體。

函數(shù)的一般定義形式如下:

類型標(biāo)識(shí)符函數(shù)名(形參表列)

形參類型聲明

{聲明部分

語(yǔ)句部分

}在定義函數(shù)時(shí)需要注意以下幾點(diǎn):

(1)函數(shù)頭部的“類型標(biāo)識(shí)符”是指函數(shù)類型,即函數(shù)返回值的數(shù)據(jù)類型。如果函數(shù)無(wú)返回值,則“類型標(biāo)識(shí)符”可以使用空類型(即void類型)。如果省略函數(shù)的類型標(biāo)識(shí)符,則默認(rèn)數(shù)據(jù)類型為int型。例如,例8.1中的函數(shù)jiec()返回值類型為int型,可以省略jiec(k)前面的類型標(biāo)識(shí)符int。

(2)

“函數(shù)名”必須為有效的標(biāo)識(shí)符(標(biāo)識(shí)符的命名規(guī)則見(jiàn)第2章)。

(3)

“形參表列”可以沒(méi)有,如果沒(méi)有則表明函數(shù)是無(wú)參函數(shù)。當(dāng)“形參表列”中有多個(gè)參數(shù)時(shí),則它們之間要用逗號(hào)(,)分隔?!靶螀㈩愋吐暶鳌鳖愃朴谧兞柯暶?。當(dāng)然,C語(yǔ)言中允許在函數(shù)的頭部與函數(shù)體之間對(duì)形參的類型進(jìn)行聲明。例如,在例8.1中函數(shù)jiec()可以定義成下面的形式:

longjiec(k)

intk;

{ints,i;

s=1;

for(i=1;i<=k;i++)s*=i;

return(s);

}

(4)

C語(yǔ)言中允許定義空函數(shù)。例如:

dummy(){}

在程序中調(diào)用此函數(shù)時(shí),實(shí)際上什么工作也不做,沒(méi)有任何實(shí)際作用。但是空函數(shù)在模塊化程序設(shè)計(jì)中是很有用的。在程序設(shè)計(jì)中往往根據(jù)需要確定若干模塊,分別由一些函數(shù)來(lái)實(shí)現(xiàn)。但在開(kāi)始時(shí),一般不可能將所有的函數(shù)模塊都設(shè)計(jì)完成,只能將一些最重要、最基本的函數(shù)設(shè)計(jì)出來(lái),而對(duì)于一些次要的函數(shù)模塊在以后需要時(shí)陸續(xù)補(bǔ)上。因此,在程序設(shè)計(jì)的開(kāi)始階段,為了程序的完整性,用一些空函數(shù)先放在那里占一個(gè)位置,以后用一個(gè)編好的函數(shù)代替空函數(shù)。這樣做,程序的結(jié)構(gòu)清楚,可讀性好,以后擴(kuò)充新功能也比較方便,對(duì)程序結(jié)構(gòu)影響不大。所以空函數(shù)在模塊化程序設(shè)計(jì)中是很有用的。

2.函數(shù)的調(diào)用

自定義函數(shù)在定義好后,必須使用才能體現(xiàn)函數(shù)定義的目的。

函數(shù)調(diào)用的一般形式如下:

函數(shù)名(實(shí)參表列);

例如,在例8.1中,計(jì)算m!的函數(shù)調(diào)用表達(dá)式是jiec(m),如果要計(jì)算5!并賦值給變量s,則調(diào)用語(yǔ)句為

s=jiec(5);函數(shù)調(diào)用的方式可以有以下三種:

(1)函數(shù)語(yǔ)句。直接由函數(shù)的調(diào)用作為一個(gè)語(yǔ)句。如例8.1中標(biāo)準(zhǔn)庫(kù)函數(shù)的調(diào)用:

printf("Pleaseinputm,n:");

這種調(diào)用方式不要求函數(shù)向主調(diào)函數(shù)帶回值,只要求完成一定的操作。

(2)函數(shù)表達(dá)式。這種調(diào)用方式是將函數(shù)調(diào)用作為一個(gè)子表達(dá)式,這種表達(dá)式稱為函數(shù)表達(dá)式,通常情況下,這種函數(shù)調(diào)用方式要求帶回一個(gè)值(即在函數(shù)中有return語(yǔ)句)參加表達(dá)式的計(jì)算。例如:

printf("\nm!/(m-n)!=%d\n",jiec(m)/jiec(m-n));

在這個(gè)調(diào)用中,jiec(m)函數(shù)調(diào)用和jiec(m-n)的調(diào)用都是作為子表達(dá)式,先將返回值代入表達(dá)式,再將兩個(gè)返回值相除得到表達(dá)式的值,最后將表達(dá)式的值輸出。

(3)函數(shù)參數(shù)。這種函數(shù)調(diào)用方式是將函數(shù)調(diào)用作為另一個(gè)函數(shù)的實(shí)參。

如果使用例8.1中的函數(shù)jiec()計(jì)算3!!,并將計(jì)算結(jié)果賦值給變量m,則可以使用下面的語(yǔ)句:

m=jiec(jiec(3));

其中jiec(3)是一次函數(shù)調(diào)用,它應(yīng)該先執(zhí)行,執(zhí)行后返回的值6作為另一次函數(shù)調(diào)用的實(shí)參,即m=jiec(6);,最后再調(diào)用函數(shù)jiec()計(jì)算出6!,并將結(jié)果賦值給變量m。

實(shí)質(zhì)上,函數(shù)調(diào)用作為函數(shù)的參數(shù),還是函數(shù)作為表達(dá)式形式調(diào)用的一種,因?yàn)楹瘮?shù)的參數(shù)本來(lái)就是表達(dá)式。

8.2.1形式參數(shù)和實(shí)際參數(shù)

在函數(shù)的定義和調(diào)用過(guò)程中會(huì)涉及到函數(shù)的參數(shù),函數(shù)的參數(shù)有兩種:形式參數(shù)和實(shí)際參數(shù),分別簡(jiǎn)稱為形參和實(shí)參。

形參是指在定義函數(shù)時(shí)函數(shù)名后面括號(hào)中的參數(shù)。而實(shí)參是指在主調(diào)函數(shù)中調(diào)用一個(gè)函數(shù)時(shí),函數(shù)名后面括號(hào)中的參數(shù)。8.2函數(shù)的參數(shù)和返回值例如,在第1章的例1.5中,函數(shù)在定義時(shí),max()函數(shù)的頭部為

floatmax(floatx,floaty)

以上就定義了兩個(gè)形參x和y,并且類型都是float型。

而在函數(shù)調(diào)用時(shí)(在main()函數(shù)中),例如:

c=max(a,b);

其中a和b作為實(shí)參。關(guān)于形參與實(shí)參作如下幾點(diǎn)說(shuō)明:

(1)形參必須是單個(gè)的變量名(普通變量名、指針變量名、一維數(shù)組、二維數(shù)組),而實(shí)參可以是常量、變量或表達(dá)式,如:

max(3,a+b);

不管實(shí)參是常量、變量還是表達(dá)式,它們必須有確定的值。

(2)在定義函數(shù)中,形參必須指定數(shù)據(jù)類型。形參的類型聲明可以放在函數(shù)名后的括號(hào)中,也可以放在函數(shù)的頭部與函數(shù)體之間(參見(jiàn)函數(shù)定義的一般形式)。例如:

floatmax(x,y)

floatx,y;

{…}

(3)形參在函數(shù)調(diào)用之前,它并不占用內(nèi)存中的存儲(chǔ)單元。只有當(dāng)函數(shù)調(diào)用時(shí),實(shí)參的值傳遞(拷貝)給形參時(shí),才給形參分配內(nèi)存存儲(chǔ)單元。在函數(shù)調(diào)用結(jié)束后,形參所占的內(nèi)存單元立即被釋放。

(4)在C語(yǔ)言中,實(shí)參向形參的數(shù)據(jù)傳遞是“單向傳遞”,即只能由實(shí)參傳遞給形參,而不能由形參傳回給實(shí)參。在實(shí)參向形參傳遞數(shù)據(jù)時(shí),是將實(shí)參的值拷貝給形參,因此,實(shí)參與形參占用不同的內(nèi)存單元。如果形參是數(shù)組名,則傳遞的是數(shù)組首地址而不是數(shù)組中元素的值。

(5)實(shí)參與形參的個(gè)數(shù)必須相同、順序一致,并且實(shí)參與對(duì)應(yīng)的形參類型應(yīng)相同或賦值兼容。例如,在例8.1中形參與實(shí)參都為int型,這是合法的、正確的。如果實(shí)參為int型,而形參為實(shí)型,或者相反,則按不同類型數(shù)值的賦值規(guī)則進(jìn)行轉(zhuǎn)換。如實(shí)參值為6.5,而形參值為int型,則會(huì)將實(shí)參6.5的值轉(zhuǎn)換成int型的值6,再送給形參。除實(shí)型與整型數(shù)據(jù)之間可轉(zhuǎn)換外,字符型與整型數(shù)據(jù)之間也能相互轉(zhuǎn)換。8.2.2函數(shù)的返回值

在調(diào)用函數(shù)過(guò)程中,經(jīng)常希望得到一個(gè)從被調(diào)用函數(shù)中帶回來(lái)的值,這就是函數(shù)的返回值。例如,在例8.1中,jiec(m)就要帶回來(lái)一個(gè)m!的值。

在C程序中,函數(shù)返回值是通過(guò)如下格式的語(yǔ)句帶回來(lái)的:

return(表達(dá)式);

當(dāng)然上述語(yǔ)句也可以是這樣的形式:

return表達(dá)式;說(shuō)明:

(1)

return語(yǔ)句括號(hào)中(或其后)的表達(dá)式也可以沒(méi)有,當(dāng)沒(méi)有這個(gè)表達(dá)式時(shí),說(shuō)明只是要求函數(shù)返回,但不要求帶回一個(gè)值,其功能就是中斷被調(diào)用函數(shù)的執(zhí)行,返回主調(diào)用函數(shù),并不帶回返回值。這種用法類似于exit()函數(shù)。

(2)只有當(dāng)主調(diào)函數(shù)中需要得到一個(gè)從被調(diào)用函數(shù)帶回來(lái)的值時(shí),才在被調(diào)用函數(shù)中使用return語(yǔ)句帶回一個(gè)返回值。即一個(gè)函數(shù)中如果有return語(yǔ)句,并且return語(yǔ)句后的表達(dá)式不是空的,則調(diào)用該函數(shù)時(shí)一定會(huì)得到一個(gè)返回值。

當(dāng)然,在函數(shù)中可能不止一個(gè)return語(yǔ)句,但每次調(diào)用函數(shù)時(shí),必定只執(zhí)行其中一個(gè)return語(yǔ)句。例如:

if(x>0)

return(1);

else

return(0);

這段程序中,當(dāng)滿足x>0時(shí),返回1;否則,返回0。

(3)函數(shù)返回值的類型。

如果函數(shù)有返回值,則這個(gè)值必定屬于一個(gè)確定的數(shù)據(jù)類型,這個(gè)類型是在函數(shù)定義的頭部說(shuō)明的。例如,在例8.1中的函數(shù)jiec()定義時(shí):

intjiec(k)

intk;

{…}

在函數(shù)名之前的int就是指出了該函數(shù)返回值的類型。因?yàn)檫@個(gè)函數(shù)返回值類型是int型,在定義時(shí)也可以省略。如果函數(shù)類型和return語(yǔ)句中表達(dá)式的值的類型不一致,則以函數(shù)類型為準(zhǔn)。對(duì)于數(shù)值型數(shù)據(jù),可以自動(dòng)進(jìn)行類型轉(zhuǎn)換。例如,若定義的返回值類型是int型,而return語(yǔ)句中表達(dá)式的類型是float型,則會(huì)將表達(dá)式的值自動(dòng)轉(zhuǎn)換成int型返回。

如果函數(shù)中沒(méi)有return語(yǔ)句,則說(shuō)明并不帶回一個(gè)值,這種情況下,在函數(shù)定義時(shí),函數(shù)名前的類型可以使用void型。void型是指空類型,即在函數(shù)定義時(shí),如果函數(shù)名前的類型是void型,則說(shuō)明該函數(shù)在調(diào)用時(shí)并不帶回一個(gè)確定的值。

(4)函數(shù)返回值,每次只能帶回一個(gè)值。盡管在函數(shù)中可能有多個(gè)return語(yǔ)句,但只有其中的一個(gè)return語(yǔ)句會(huì)執(zhí)行,也就只能帶回一個(gè)值。如果想從函數(shù)中帶回多個(gè)值,可以采用的方式有兩種:①使用指針變量,通過(guò)地址傳遞方式;②使用全局變量。這些在后面將會(huì)介紹到。

(5)當(dāng)函數(shù)返回值是指針值時(shí),函數(shù)頭部的定義形式一般如下:

數(shù)據(jù)類型*函數(shù)名(參數(shù)表列)

這種情況下,在主調(diào)用函數(shù)中,函數(shù)返回值所賦給的變量必須是指針變量。

例8.2

下面程序中有四處錯(cuò)誤,請(qǐng)根據(jù)題意改正。

主函數(shù)接收從鍵盤(pán)上輸入的一組整數(shù)(以-999作為輸入結(jié)束)并保存到整型數(shù)組xx中,調(diào)用fun函數(shù)。函數(shù)fun的功能是:對(duì)長(zhǎng)度為n的數(shù)組a,不考慮這組整數(shù)中的最大數(shù)和最小數(shù)(其有重復(fù),則都不考慮),求出余下數(shù)中的最大數(shù)max及最大數(shù)的個(gè)數(shù)cnt1、最小數(shù)min及最小數(shù)的個(gè)數(shù)cnt2,分別保存到全局變量max、cnt1、min、cnt2中。含有錯(cuò)誤的源程序如下:

#include<stdio.h>

#defineN100

intmax,min,cnt1,cnt2;

intfun(inta[],intn)

{inti,j,k,x;

for(i=0;i<n;i++)

{k=i;

for(j=i+1;j<=N;j++)

if(a[k]<a[j])k=j;

if(k!=i){x=a[i];a[i]=a[k];a[k]=x;}

}

for(i=0;i<N;i++)printf("%5d",a[i]);

printf("\n");

i=1;

j=N-2;

while(a[i]==a[i-1])i++;

while(a[j]==a[j+1])j--;

max=a[i];min=a[j];cnt1=cnt2=1;

while(a[i+1]==a[i]){cnt1++;i++;}

while(a[j-1]==a[j]){cnt1++;i++;}

}

main()

{intxx[N],i=0,m=0;

scanf("%d",&xx[i]);

while(xx[i++]!=-999)scanf("%d",&xx[i]);

m=i-1;

fun(m,xx);

for(i=0;i<m;i++)printf((i+1)%5?"%5d":"%5d\n",xx(i));

printf("\n");

printf("cmax=%d,cmin=%d,cnt1=%d,cnt2=%d\n",max,min,cnt1,cnt2);

}本例中共四處錯(cuò)誤,第一處在fun()函數(shù)中,由于fun()函數(shù)并沒(méi)有返回值,因此,函數(shù)名fun前的int型不對(duì),應(yīng)改成void;第二處也在fun()函數(shù)中,由于數(shù)組a共有N個(gè)元素,但在內(nèi)層的for循環(huán)中,表達(dá)式2為j<=N,當(dāng)j=N時(shí),數(shù)組就會(huì)越界;第三處在主函數(shù)中,fun(m,xx);對(duì)函數(shù)進(jìn)行調(diào)用時(shí),實(shí)參順序與函數(shù)fun()中的形參順序不一致,所以應(yīng)改成fun(xx,m);;第四處錯(cuò)誤也在主函數(shù)中,在主函數(shù)for循環(huán)中,輸出的應(yīng)是xx數(shù)組的各元素,但其調(diào)用數(shù)組是用xx(i),在C語(yǔ)言中,調(diào)用函數(shù)應(yīng)用中括號(hào),因此應(yīng)改成xx[i]。

通過(guò)本例我們可以看出,函數(shù)在調(diào)用時(shí)特別要注意:形參與實(shí)參的對(duì)應(yīng)(個(gè)數(shù)、類型、順序的對(duì)應(yīng));函數(shù)返回值的類型與函數(shù)中定義類型的一致性。

8.3.1形參與實(shí)參的結(jié)合方式

函數(shù)在定義與調(diào)用過(guò)程中,如果需要在主調(diào)函數(shù)與被調(diào)用函數(shù)之間進(jìn)行數(shù)據(jù)的傳遞,例如:主調(diào)函數(shù)要向被調(diào)用函數(shù)傳過(guò)去實(shí)參的值,而被調(diào)用函數(shù)執(zhí)行完后有可能要將運(yùn)算結(jié)果帶回到主調(diào)函數(shù)中。這就涉及到被調(diào)用函數(shù)中的形參與主調(diào)函數(shù)中的實(shí)參互相結(jié)合的問(wèn)題。在C程序中,一般來(lái)說(shuō),形參與實(shí)參的結(jié)合方式有以下兩種。8.3函數(shù)間的參數(shù)傳遞

1.數(shù)值傳遞

所謂數(shù)值傳遞,就是指主調(diào)函數(shù)中的實(shí)參地址與被調(diào)用函數(shù)中的形參地址是互相獨(dú)立的,在函數(shù)調(diào)用時(shí),直接將實(shí)參值拷貝給形參在內(nèi)存中的存儲(chǔ)單元中。在這種結(jié)合方式下,被調(diào)用函數(shù)在執(zhí)行過(guò)程中,當(dāng)需要存取形參值時(shí),直接存取形參地址中的數(shù)據(jù),而不影響實(shí)參地址中的值。因此,如果在被調(diào)用函數(shù)中改變了形參的值,是不會(huì)改變實(shí)參值的,因?yàn)樾螀⒑蛯?shí)參的地址是互不相同的。顯然,當(dāng)被調(diào)用函數(shù)執(zhí)行完返回主調(diào)函數(shù)時(shí),被調(diào)用函數(shù)中形參的新值也不會(huì)傳回到主調(diào)用函數(shù)。由此可以看出,在形參與實(shí)參為數(shù)值傳遞的方式下,被調(diào)用函數(shù)中對(duì)形參的操作不影響主調(diào)用函數(shù)中的實(shí)參值,因此只能實(shí)現(xiàn)數(shù)據(jù)的單向傳遞,即從主調(diào)函數(shù)的實(shí)參將值傳送給被調(diào)用函數(shù)的形參。所以,這種傳遞又稱為“單向值傳遞”。

在這種方式中,實(shí)參可以是變量、常量、也可以是表達(dá)式。

在C語(yǔ)言中,當(dāng)形參為簡(jiǎn)單變量(即整型、實(shí)型、字符型)時(shí),均采用數(shù)值傳遞。在這種情況下,一個(gè)函數(shù)只能返回一個(gè)函數(shù)值,在例8.3中就很好地說(shuō)明了這個(gè)問(wèn)題。

例8.3

無(wú)效的數(shù)值交換。

本例我們將驗(yàn)證:在賦值調(diào)用中,形參的改變不會(huì)對(duì)實(shí)參產(chǎn)生影響。

程序如下:

#include<stdio.h>

voidswap(intx,inty); /*聲明函數(shù)*/

main()

{inta=7,b=14;

printf("數(shù)值交換\n\n");

printf("未交換前:a=%d,b=%d\n",a,b);

swap(a,b);

printf("調(diào)用函數(shù)swap(a,b)后:a=%d,b=%d\n",a,b);

}

voidswap(x,y)

intx,y;

{inttemp;

temp=x; /*開(kāi)始交換形參內(nèi)容*/

x=y;

y=temp;

printf("在函數(shù)swap()中:x=%d,y=%d\n",x,y);

}程序運(yùn)行結(jié)果如下:

數(shù)值交換

未交換前:a=7,b=14

在函數(shù)swap()中:x=14,y=7

調(diào)用函數(shù)swap(a,b)后:a=7,b=14

在上面的程序中,主函數(shù)中定義了兩個(gè)變量,即a=7,b=14,并分別賦了值,當(dāng)調(diào)用swap()函數(shù)時(shí),只是將實(shí)參a的值復(fù)制給形參x,將實(shí)參b的值復(fù)制給y。由于此時(shí)a和x、b和y分別占用不同的內(nèi)存單元,在swap()函數(shù)中,將形參x和y的值互換,這只是在x和y的內(nèi)存單元內(nèi)的值互換,但對(duì)a和b存儲(chǔ)單元中的值并不產(chǎn)生影響,當(dāng)swap()函數(shù)執(zhí)行結(jié)束時(shí)會(huì)釋放x和y的存儲(chǔ)單元,交換后的形參值丟失。所以,在主函數(shù)中最后輸出a和b的值仍然是7和14,并沒(méi)有交換。

2.地址傳遞

所謂地址傳遞,是指在一個(gè)函數(shù)調(diào)用另一個(gè)函數(shù)時(shí),并不是將主調(diào)函數(shù)中的實(shí)參值直接傳送給被調(diào)用函數(shù)中的形參,而只是將存放實(shí)參的存儲(chǔ)單元地址傳送給形參。在這種結(jié)合方式下,被調(diào)用函數(shù)在執(zhí)行過(guò)程中,當(dāng)需要存取形參值時(shí),實(shí)際上是通過(guò)對(duì)形參(形參是一個(gè)指針或地址)指向的存儲(chǔ)單元進(jìn)行數(shù)據(jù)存取,而此時(shí)形參內(nèi)存放的值(地址)與實(shí)參存放的值(地址)相同,也就是說(shuō),形參與實(shí)參指向同一塊存儲(chǔ)單元。因此,如果在被調(diào)用函數(shù)中改變了形參指向存儲(chǔ)單元的值,實(shí)際上也就改變了主調(diào)函數(shù)中實(shí)參所指向存儲(chǔ)單元的值。由此可見(jiàn),在這種方式下,被調(diào)用函數(shù)中對(duì)形參所指向存儲(chǔ)單元的操作實(shí)際上就是對(duì)主調(diào)函數(shù)中實(shí)參所指向存儲(chǔ)單元的操作。顯然,當(dāng)被調(diào)用函數(shù)執(zhí)行完成時(shí),形參空間釋放,丟失的是形參中存放的地址,但是形參所指向的存儲(chǔ)單元并不釋放,因此,對(duì)形參的操作就保留了下來(lái),在主調(diào)函數(shù)中,又可以通過(guò)實(shí)參中的地址訪問(wèn)經(jīng)被調(diào)用函數(shù)修改后的存儲(chǔ)單元的值。所以這種傳遞方式稱為“地址傳遞”。

在這種傳遞方式中,實(shí)參可以是變量的地址或指針變量。

在C語(yǔ)言中,當(dāng)形參為數(shù)組名或指針變量時(shí),均采用地址傳遞。

如果我們將例8.3中的程序進(jìn)行如下修改:

#include<stdio.h>

voidswap(int*x,int*y); /*聲明函數(shù)*/

main()

{inta=7,b=14;

printf("數(shù)值交換\n\n");

printf("未交換前:a=%d,b=%d\n",a,b);

swap(&a,&b);

printf("調(diào)用函數(shù)swap(a,b)后:a=%d,b=%d\n",a,b);

}

voidswap(x,y)

int*x,*y;

{inttemp;

temp=*x; /*開(kāi)始交換形參內(nèi)容*/

*x=*y;

*y=temp;

printf("在函數(shù)swap()中:*x=%d,*y=%d\n",*x,*y);

}

程序運(yùn)行結(jié)果如下:

數(shù)值交換

未交換前:a=7,b=14

在函數(shù)swap()中:*x=14,*y=7

調(diào)用函數(shù)swap(a,b)后:a=14,b=7在這個(gè)程序中,實(shí)現(xiàn)了數(shù)值的交換。在調(diào)用swap()函數(shù)時(shí),傳遞給它的是變量a和b的地址。在swap()函數(shù)定義中,形參定義成指針類型,當(dāng)調(diào)用時(shí),將變量a的地址拷貝給指針變量x,而將變量b的地址拷貝給指針變量y,在swap()函數(shù)中的交換是針對(duì)指針變量x和y所指向單元的值的互換,即交換了主調(diào)函數(shù)中變量a、b的值。

在地址傳遞方式中,我們常見(jiàn)到使用數(shù)組名作為實(shí)參與形參,實(shí)際上,也是通過(guò)將實(shí)參數(shù)組的首地址傳遞給被調(diào)用函數(shù)的形參數(shù)組,在被調(diào)用函數(shù)中對(duì)數(shù)組名所指向的若干連續(xù)存儲(chǔ)單元進(jìn)行操作,即是對(duì)實(shí)參數(shù)組進(jìn)行操作,返回后能夠在主調(diào)函數(shù)中得到處理后的數(shù)組元素值。下面通過(guò)例子來(lái)說(shuō)明。

例8.4

從鍵盤(pán)輸入10個(gè)整數(shù),通過(guò)調(diào)用一個(gè)函數(shù)將這10個(gè)數(shù)使用直接選擇排序法從小到大排序,再輸出這10個(gè)整數(shù)。

問(wèn)題分析:本例中要求有兩個(gè)函數(shù),子函數(shù)(sort())用于將10個(gè)數(shù)排序,主函數(shù)負(fù)責(zé)輸入和輸出10個(gè)整數(shù)。

程序如下:

#include<stdio.h>

#defineN10

voidsort(inta[]) /*對(duì)10個(gè)整數(shù)進(jìn)行直接選擇排序*/

{inti,j,k,temp;

for(i=0;i<N;i++)

{k=i;

for(j=i+1;j<N;j++)

if(a[k]>a[j])k=j;

if(k!=i) /*如果最小值不是下標(biāo)為i的元素,交換最小 值和當(dāng)前位置的值*/

{temp=a[i];a[i]=a[k];a[k]=temp;}

}

}

main()

{inti,arr[N];

printf("請(qǐng)輸入10個(gè)整數(shù):\n");

for(i=0;i<N;i++)scanf("%d",&arr[i]);

sort(arr); /*調(diào)用排序函數(shù),用一維數(shù)組名作為實(shí)參*/

printf("\n排序后的10整數(shù)是:\n");

for(i=0;i<N;i++)

{printf("%d",arr[i]);

if((i+1)%5==0)printf("\n"); /*每行輸出5個(gè)整數(shù)*/

}

}

在本例中,實(shí)參和形參都是使用的一維數(shù)組名,即一維數(shù)組的起始地址。實(shí)參向形參傳遞的數(shù)據(jù)是一個(gè)地址。下面再列舉一個(gè)以二維數(shù)組的數(shù)組名作為參數(shù)傳遞的例子。

例8.5

有一個(gè)4

×

3的矩陣,求所有元素中的最小值。

問(wèn)題分析:本例可以通過(guò)兩個(gè)函數(shù)來(lái)實(shí)現(xiàn),子函數(shù)求最小值,而主函數(shù)負(fù)責(zé)輸入/輸出矩陣中的值(二維數(shù)組元素值)。在這兩個(gè)函數(shù)之間傳遞的數(shù)據(jù)是二維數(shù)組名。

程序如下:

#include<stdio.h>

intmin_value(inta[][3])

{inti,j,min;

min=a[0][0];/*先將第1個(gè)元素的值當(dāng)成最小值*/

for(i=0;i<4;i++) /*將其他元素與最小值min進(jìn)行比較,如果其 他元素的值比min小,就將其值賦值給min*/

for(j=0;j<3;j++)

if(a[i][j]<min)min=a[i][j];

return(min);

}

main()

{inti,j,arr[4][3];

printf("請(qǐng)輸入4×3的矩陣\n");

for(i=0;i<4;i++)

for(j=0;j<3;j++)

scanf("%d",&arr[i][j]);

printf("\n矩陣中最小值為:%d",min_value(arr));

}

本例是用二維數(shù)組的名字作為實(shí)參和形參,需要注意的是:在min_value()函數(shù)定義時(shí),作為形參的二維數(shù)組第二維的長(zhǎng)度不能少。8.3.2變量的作用域

變量的作用域是指變量的作用范圍,在這個(gè)范圍內(nèi),變量是有效存在的。超出這個(gè)范圍時(shí),為變量分配的存儲(chǔ)空間將被釋放,在范圍外引用變量是非法的,就會(huì)產(chǎn)生錯(cuò)誤。

根據(jù)變量作用域的不同,可將其分成局部變量和全局變量。

1.局部變量

在函數(shù)內(nèi)部定義的變量稱為局部變量,又稱內(nèi)部變量。函數(shù)內(nèi)部定義的變量作用域從定義位置開(kāi)始,到本函數(shù)結(jié)束為止(主函數(shù)main()也不例外)。因此,不同函數(shù)中的局部變量可以重名,互相獨(dú)立。特別指出的是,函數(shù)中的形參也是局部變量。例如,例8.3中,主函數(shù)中定義的變量a、b是局部變量,它們只在主函數(shù)內(nèi)有效;而函數(shù)swap()中的形參x、y以及在swap()函數(shù)中定義的變量temp也都是局部變量,它們只在函數(shù)swap()中有效。

注意:在復(fù)合語(yǔ)句(由{}括起來(lái)的語(yǔ)句)中定義的變量也是局部變量,但其作用域是從定義位置到本復(fù)合語(yǔ)句結(jié)束。

2.全局變量

一個(gè)C源程序文件可以包含一個(gè)或若干個(gè)函數(shù)。在函數(shù)內(nèi)定義的變量是局部變量,而在函數(shù)之外定義的變量則稱為全局變量,又稱外部變量。全局變量的作用域從定義變量的位置開(kāi)始到本源程序文件結(jié)束。

例如,在例8.2中,函數(shù)fun()之前的語(yǔ)句“intmax,min,cnt1,cnt2;”就是定義全局變量max、min、cnt1、cnt2,因此其作用域包括了fun()和main()函數(shù)。在例8.3中并沒(méi)有實(shí)際數(shù)值的交換,如果使用全局變量,就可以實(shí)現(xiàn)數(shù)值的交換,即程序修改如下:

#include<stdio.h>

voidswap(intx,inty); /*聲明函數(shù)*/

intx,y; /*定義全局變量x、y*/

main()

{x=7;

y=14;

printf("數(shù)值交換\n\n");

printf("未交換前:x=%d,b=%d\n",x,y);

swap();

printf("調(diào)用函數(shù)swap()后:x=%d,y=%d\n",x,y);

}

voidswap()

{inttemp;

temp=x; /*開(kāi)始交換全局變量x和y的值*/

x=y;

y=temp;

printf("在函數(shù)swap()中:x=%d,y=%d\n",x,y);

}

在上述程序中,變量x和y在程序中的開(kāi)頭進(jìn)行定義,它們?cè)诤竺鎯蓚€(gè)函數(shù)swap()和main()中都起作用。該程序先在main()函數(shù)中給全局變量x、y賦值,再在swap()函數(shù)中將它們的值互換,這樣就可以達(dá)到數(shù)值交換的目的。原因是swap()函數(shù)中的變量x和y與主函數(shù)中的變量x和y均是同一個(gè)變量,即使用同一塊內(nèi)存單元。

全局變量的作用范圍是定義變量的位置開(kāi)始到本源程序文件結(jié)束。但如果在全局變量的作用域內(nèi),有與其同名的局部變量,則在該局部變量的作用域內(nèi),全局變量不起作用(即變量屏蔽)。

例8.6

全局變量與局部變量同名示例。

有如下程序:

intx=1;

main()

{inty;

intf(int); /*聲明函數(shù)f()*/

y=f(3);printf("%d\n%d",x,y);

}

intf(intx)

{if(x==3)

{intx=2;

returnx;

}

elsereturnx;

}

該程序運(yùn)行后,輸出x的值是_________,y的值是_________。程序分析:

在該程序中有兩個(gè)函數(shù),在程序開(kāi)始處定義了全局變量x,并賦值為1。而在函數(shù)f()中形參為x,并且在復(fù)合語(yǔ)句中用“intx=2;”還定義了局部變量x,它們各自的作用域見(jiàn)程序中。在復(fù)合語(yǔ)句中定義的變量x的作用域內(nèi),形參局部變量x和全局變量x都不起作用。而在函數(shù)f()中,除復(fù)合語(yǔ)句外的部分,形參變量x起作用,而全局變量x不起作用。因此,在主函數(shù)中輸出時(shí)的變量x應(yīng)該是全局變量x,第一個(gè)空應(yīng)該填1;而“y=f(3);”語(yǔ)句在調(diào)用函數(shù)f()時(shí),執(zhí)行的是復(fù)合語(yǔ)句部分,因此返回值應(yīng)是2,y的值應(yīng)是2,第二個(gè)空應(yīng)填2。8.3.3動(dòng)態(tài)存儲(chǔ)變量與靜態(tài)存儲(chǔ)變量

除了變量的作用域外,C語(yǔ)言中還提供了變量的存儲(chǔ)類型來(lái)對(duì)變量作用域進(jìn)行說(shuō)明。各種變量的作用域不同,就其本質(zhì)來(lái)說(shuō)是因?yàn)樽兞康拇鎯?chǔ)類型不同。對(duì)一個(gè)變量的說(shuō)明,不僅應(yīng)說(shuō)明其數(shù)據(jù)類型,還應(yīng)說(shuō)明其存儲(chǔ)類型。數(shù)據(jù)類型如int、float、char等。

C語(yǔ)言中,變量的存儲(chǔ)類型分為靜態(tài)存儲(chǔ)和動(dòng)態(tài)存儲(chǔ)兩大類。對(duì)變量的存儲(chǔ)類型說(shuō)明有以下四種:自動(dòng)變量(auto)、寄存器變量(register)、外部變量(extern)和靜態(tài)變量(static)。自動(dòng)變量和寄存器變量屬于動(dòng)態(tài)存儲(chǔ)方式,外部變量和靜態(tài)變量屬于靜態(tài)存儲(chǔ)方式。

1.內(nèi)部變量

內(nèi)部變量又稱局部變量,是指在函數(shù)內(nèi)部定義的變量。

1)靜態(tài)變量

靜態(tài)變量是在變量定義時(shí)就分配存儲(chǔ)單元,該存儲(chǔ)單元一直保持不變,直到整個(gè)程序結(jié)束才釋放存儲(chǔ)單元,但其作用域只是在定義靜態(tài)變量的函數(shù)內(nèi)。

靜態(tài)變量的定義格式如下:

static數(shù)據(jù)類型變量名表列;

說(shuō)明:

(1)靜態(tài)變量的作用域與生存期不相同。靜態(tài)變量是一種比較特殊的變量。在定義開(kāi)始時(shí),給其分配存儲(chǔ)空間,供其在調(diào)用時(shí)使用,函數(shù)調(diào)用結(jié)束時(shí)并不釋放存儲(chǔ)空間,而是直到程序結(jié)束才釋放存儲(chǔ)空間。

(2)靜態(tài)變量在定義時(shí)賦初值實(shí)際上是發(fā)生在程序編譯時(shí)。如果在定義靜態(tài)變量后退出它的作用域時(shí),該變量依然存在,下次進(jìn)入作用域時(shí)就不用再進(jìn)行定義和初始化,不再重新賦初值,只是保留上次調(diào)用結(jié)束時(shí)的值。

(3)如果靜態(tài)變量定義時(shí)沒(méi)有賦初值,int型變量默認(rèn)初值為0,float型變量默認(rèn)初值為0.0,char型變量默認(rèn)初值為'\0',等等。

(4)形參不能定義成靜態(tài)變量。靜態(tài)局部變量經(jīng)常使用,但建議盡可能少用,因?yàn)樗鼈冋加玫拇鎯?chǔ)空間直到程序結(jié)束才釋放。

例8.7

利用靜態(tài)變量實(shí)現(xiàn):計(jì)算并輸出1~5的階乘值。

程序如下:

#include<stdio.h>

longfun(intn)

{staticlongs=1;

s=s*n;

return(s);

}

main()

{inti;

for(i=1;i<=5;i++)

printf("\n%d!=%ld",i,fun(i));

}程序運(yùn)行結(jié)果:

1!=1

2!=2

3!=6

4!=24

5!=120

在函數(shù)fun()中,變量s定義為靜態(tài)局部變量,因此,在程序編譯時(shí)為它賦初值1。在每次調(diào)用fun()函數(shù)時(shí)靜態(tài)變量s均保留上次調(diào)用時(shí)得到的值,在此基礎(chǔ)上再進(jìn)行s=s*n的計(jì)算。例如,計(jì)算5!是在計(jì)算的4!值(存放在s中)上再乘以5。

試想如果將上述程序中的“static”去掉,程序輸出結(jié)果會(huì)是什么樣?

2)自動(dòng)變量

自動(dòng)變量就是動(dòng)態(tài)變量,在程序執(zhí)行過(guò)程中,需要使用時(shí)才分配存儲(chǔ)單元,使用完立即釋放空間。例如函數(shù)的形參,在函數(shù)定義時(shí)并不給形參分配存儲(chǔ)空間,只是在函數(shù)被調(diào)用時(shí)才為其分配存儲(chǔ)空間,當(dāng)函數(shù)調(diào)用結(jié)束后,自動(dòng)釋放存儲(chǔ)空間。

動(dòng)態(tài)變量的定義格式如下:

auto數(shù)據(jù)類型變量名表列;/*關(guān)鍵字auto可省略*/

說(shuō)明:

(1)在定義時(shí),關(guān)鍵字auto可省略,本節(jié)以前所定義的局部變量都省略了auto,則說(shuō)明它們都是自動(dòng)變量。

(2)在函數(shù)中定義的自動(dòng)變量,只在該函數(shù)內(nèi)有效;在函數(shù)被調(diào)用時(shí)分配存儲(chǔ)空間,調(diào)用結(jié)束就釋放。函數(shù)在復(fù)合語(yǔ)句中定義的自動(dòng)變量,只在該復(fù)合語(yǔ)句中有效;退出復(fù)合語(yǔ)句后,也不能再使用,否則將引起錯(cuò)誤。

(3)如果只定義動(dòng)態(tài)變量而不賦初值,則其初值是不確定的(即是一個(gè)隨機(jī)值)。如果在定義時(shí)就賦初值,則賦初值操作是在調(diào)用時(shí)進(jìn)行的,且每次調(diào)用都要重新賦一次初值。這一點(diǎn)與靜態(tài)變量有很大區(qū)別。

(4)自動(dòng)變量的作用域和生存期是一致的,都局限于定義它的函數(shù)或復(fù)合語(yǔ)句內(nèi),因此在不同的函數(shù)或復(fù)合語(yǔ)句內(nèi)就可以使用同名的自動(dòng)變量。

3)寄存器變量

通常變量的值都是存儲(chǔ)在內(nèi)存中的,但有些變量由于要大量重復(fù)使用(如for循環(huán)中的循環(huán)記數(shù)變量),為了提高執(zhí)行效率,將這樣的變量存放在CPU的寄存器中,這種變量就稱為寄存器變量。

寄存器變量的定義格式如下:

register數(shù)據(jù)類型變量名表列;下面的程序中使用了寄存器變量來(lái)作為循環(huán)計(jì)數(shù)變量。

main()

{intsum=0;

registerinti;

for(i=1;i<100;i++)sum+=i;

printf("%d",sum);

}說(shuō)明:

(1)只有局部變量才能定義為寄存器變量。

(2)如果系統(tǒng)不支持寄存器變量,或者CPU中的寄存器不夠時(shí),實(shí)際上是將寄存器變量當(dāng)作自動(dòng)變量處理。

(3)由于CPU中的寄存器數(shù)目有限,不能定義過(guò)多的寄存器變量,一般不超過(guò)3個(gè)為宜,并且寄存器變量使用完后立即釋放。

(4)由于寄存器變量不在內(nèi)存中,因此不能進(jìn)行地址運(yùn)算。例如:

registerintk;

scanf("%d",&k);

這種使用方式就不對(duì)。

4)自動(dòng)變量、靜態(tài)變量和寄存器變量的比較

自動(dòng)變量、靜態(tài)變量和寄存器變量的比較如表8.1所示。

表8.1自動(dòng)變量、靜態(tài)變量和寄存器變量比較2.外部變量

外部變量是在函數(shù)外部定義的變量,也就是全局變量。

外部變量的定義格式如下:

extern數(shù)據(jù)類型變量名表列;

全局變量如果在源程序文件開(kāi)頭定義,則在整個(gè)文件范圍內(nèi)的所有函數(shù)都可以使用該變量。但如果不在文件開(kāi)頭定義全局變量,則只限于在定義點(diǎn)到文件結(jié)束范圍內(nèi)的函數(shù)使用該變量。如果在全局變量的作用域外還要使用全局變量,則應(yīng)事先用extern加以說(shuō)明。由此可以看出,使用extern說(shuō)明可以擴(kuò)展全局變量的作用域。擴(kuò)展全局變量作用域的方式可以分成兩種:一是在同一文件內(nèi)擴(kuò)展全局變量作用域;二是將一個(gè)文件中的全局變量作用域擴(kuò)展到另一個(gè)文件。

1)同一文件內(nèi)擴(kuò)展全局變量作用域

在同一文件中,由于全局變量不是在文件開(kāi)頭就定義的,因此,如果想在全局變量定義點(diǎn)之前的函數(shù)中使用全局變量,則應(yīng)在函數(shù)中用extern加以說(shuō)明。例如,下列程序可以實(shí)現(xiàn)兩個(gè)變量值的交換。

#include<stdio.h>

voidswap(intx,inty); /*聲明函數(shù)*/

main()

{externintx,y; /*聲明全局變量,說(shuō)明變量x、y在后 面有定義*/

x=7; /*給全局變量x賦初值*/

y=14; /*給全局變量y賦初值*/

printf("數(shù)值交換\n\n");

printf("未交換前:x=%d,b=%d\n",x,y);

swap();

printf("調(diào)用函數(shù)swap()后:x=%d,y=%d\n",x,y);

}

intx,y; /*定義全局變量x和y*/

voidswap()

{inttemp;

temp=x; /*開(kāi)始交換全局變量x和y的值*/

x=y;

y=temp;

printf("在函數(shù)swap()中:x=%d,y=%d\n",x,y);

}上述程序中,雖然全局變量x和y的定義在主函數(shù)后面,但由于在主函數(shù)中用extern聲明了變量x和y是外部變量,因此,將全局變量x和y的作用域擴(kuò)展到了主函數(shù),這樣在主函數(shù)中就可以對(duì)全局變量x和y進(jìn)行操作(賦值、輸出)。

2)全局變量的作用域擴(kuò)展到另一個(gè)文件中

當(dāng)需要在一個(gè)源程序文件中使用另一個(gè)源程序文件中定義的全局變量時(shí),也可以用extern對(duì)全局變量進(jìn)行聲明。例如,在下列程序中,兩個(gè)函數(shù)分別存放在兩個(gè)文件中。

/*file1.c文件*/

#include<stdio.h>

#include"file2.c" /*將源程序文件file2.c包含到當(dāng)前文件中*/

intx,y; /*定義全局變量*/

main()

{x=7; /*給全局變量x賦初值*/

y=14; /*給全局變量y賦初值*/

printf("數(shù)值交換\n\n");

printf("未交換前:x=%d,b=%d\n",x,y);

swap();

printf("調(diào)用函數(shù)swap()后:x=%d,y=%d\n",x,y);

}

/*file2.c文件*/

externintx,y; /*聲明全局變量*/

voidswap()

{inttemp;

temp=x; /*開(kāi)始交換全局變量x和y的值*/

x=y;

y=temp;

printf("在函數(shù)swap()中:x=%d,y=%d\n",x,y);

return;

}

在主函數(shù)所在的文件file1.c中定義了全局變量x和y,在函數(shù)swap()所在的文件file2.c中將x和y聲明為外部變量,這樣在函數(shù)swap()中就可以使用文件file1.c中定義的全局變量x和y了。

特別需要說(shuō)明的是,同一源程序文件中不能同時(shí)定義相同的全局變量。例如,下面的程序是錯(cuò)誤的。

#include<stdio.h>

voidswap(intx,inty); /*聲明函數(shù)*/

intx,y; /*定義全局變量*/

main()

{

x=7; /*給全局變量x賦初值*/

y=14; /*給全局變量y賦初值*/

printf("數(shù)值交換\n\n");

printf("未交換前:x=%d,b=%d\n",x,y);

swap();

printf("調(diào)用函數(shù)swap()后:x=%d,y=%d\n",x,y);

}

intx,y;/*重復(fù)定義全局變量x和y,與前面的定義相沖突*/

voidswap()

{inttemp;

temp=x;/*開(kāi)始交換全局變量x和y的值*/

x=y;

y=temp;

printf("在函數(shù)swap()中:x=%d,y=%d\n",x,y);

}

如果想使在文件中定義的全局變量不被其他文件使用,就可以在全局變量定義時(shí),將其定義成static型外部變量。例如,下面程序就是錯(cuò)誤的。

/*file1.c文件*/

#include<stdio.h>

#include"file2.c" /*將源程序文件file2.c包含到當(dāng)前文件中來(lái)*/

staticintx,y; /*定義靜態(tài)外部變量,僅限文件file1.c中使用 */

main()

{x=7; /*給全局變量x賦初值*/

y=14; /*給全局變量y賦初值*/

printf("數(shù)值交換\n\n");

printf("未交換前:x=%d,b=%d\n",x,y);

swap();

printf("調(diào)用函數(shù)swap()后:x=%d,y=%d\n",x,y);

}

/*file2.c文件*/

externintx,y; /*聲明全局變量,程序編譯時(shí),會(huì)提示變量x 和y沒(méi)有定義*/

voidswap()

{inttemp;

temp=x; /*開(kāi)始交換全局變量x和y的值*/

x=y;

y=temp;

printf("在函數(shù)swap()中:x=%d,y=%d\n",x,y);

return;

}

上述程序在編譯連接時(shí)會(huì)提示“變量x,y沒(méi)有定義”的錯(cuò)誤,這是因?yàn)?,在主函?shù)所在的文件file1.c中定義了外部變量x和y,但它們只能在文件file1.c中使用,而不能將其作用域擴(kuò)展到文件file2.c中。8.3.4內(nèi)部函數(shù)和外部函數(shù)

定義函數(shù)就是為了被其他函數(shù)調(diào)用,因此,在本質(zhì)上函數(shù)是全局的,是能夠被不同文件中的函數(shù)所調(diào)用。但是,也可以指定函數(shù)不能被其他文件中的函數(shù)調(diào)用,只能被本文件中的函數(shù)調(diào)用。根據(jù)函數(shù)能否被其他源文件中的函數(shù)調(diào)用,將函數(shù)分為內(nèi)部函數(shù)和外部函數(shù)。

1.內(nèi)部函數(shù)

只能被本文件中其他函數(shù)調(diào)用的函數(shù)稱為內(nèi)部函數(shù),內(nèi)部函數(shù)又稱為靜態(tài)函數(shù)。

內(nèi)部函數(shù)的定義形式如下:

static類型標(biāo)識(shí)符函數(shù)名(形參表)

例如,staticintfunc(n)就是將函數(shù)func()定義成一個(gè)內(nèi)部函數(shù),它只能被與它同在一個(gè)文件中的其他函數(shù)調(diào)用,而其他文件中的函數(shù)不能調(diào)用它。

在多人合作的模塊化程序設(shè)計(jì)中,如果在不同的文件中使用了相同名稱的內(nèi)部函數(shù),在程序聯(lián)調(diào)時(shí),不會(huì)相互干擾,這樣每個(gè)人在編寫(xiě)函數(shù)時(shí),就不必?fù)?dān)心所用函數(shù)是否會(huì)與其他源文件中的函數(shù)同名。

2.外部函數(shù)

能被其他文件中函數(shù)調(diào)用的函數(shù)稱為外部函數(shù)。

外部函數(shù)的定義形式如下:

[extern]類型標(biāo)識(shí)符函數(shù)名(形參表)

例如,externintfunc(n)或intfunc(n)中,extern說(shuō)明符可以省略,因此,如果省略extern說(shuō)明符,則默認(rèn)函數(shù)是外部函數(shù),本節(jié)之前我們所定義的函數(shù)都是外部函數(shù)。

外部函數(shù)在一個(gè)源文件中定義,而其調(diào)用范圍可以通過(guò)在其他源文件中用extern聲明,而將其調(diào)用范圍擴(kuò)展到其他源文件中。例如,下面的程序可以說(shuō)明在不同文件之間調(diào)用函數(shù)。

/*file.c文件*/

main()

{externinput_str(chars[80]);

externdel_str(chars[],charc);

externoutput_str(chars[]);

/*上面三行聲明在本函數(shù)中將要調(diào)用在其他文件中定義的三個(gè)函數(shù)*/

charstr[80],ch;

input_str(str);

scanf("%c",&ch);

del_str(str,ch);

output_str(str);

}

/*file1.c文件*/

#include<stdio.h>

input_str(chars[80]) /*定義外部函數(shù)input_str()*/

{gets(s); /*輸入字符串*/

}

/*file2.c文件*/

del_str(chars[],charc) /*定義外部函數(shù)del_str()*/

{inti,j;

for(i=j=0;s[i]!='\0';i++)

if(s[i]!=c)s[j++]=s[i];

s[j]=’\0’;

}

/*file3.c文件*/

#include<stdio.h>

output_str(chars[]) /*定義外部函數(shù)output_str()*/

{puts(s);} /*輸入字符串*/

上述程序包含4個(gè)文件,在file.c中要調(diào)用其他3個(gè)文件中定義的外部函數(shù),因此必須在主函數(shù)中對(duì)要調(diào)用的外部函數(shù)進(jìn)行聲明。

1.函數(shù)嵌套

在函數(shù)調(diào)用時(shí),允許在函數(shù)中調(diào)用另一個(gè)已聲明的函數(shù),這種在函數(shù)中調(diào)用另一個(gè)函數(shù)的用法稱為函數(shù)的嵌套。C語(yǔ)言中允許函數(shù)進(jìn)行嵌套調(diào)用,但不允許函數(shù)的嵌套定義。

下面的程序段顯示出了函數(shù)嵌套調(diào)用。

intx(); /*聲明x()函數(shù)*/

inty(); /*聲明y()函數(shù)*/

main()

{8.4函數(shù)的嵌套與遞歸調(diào)用

m=x(); /*調(diào)用函數(shù)x()*/

}

intx()

{

k=y(); /*嵌套調(diào)用函數(shù)y()*/

}

inty()

{

}

上面的程序結(jié)構(gòu)中,函數(shù)的執(zhí)行與調(diào)用過(guò)程如圖8.1所示。

圖8.1函數(shù)嵌套調(diào)用的執(zhí)行過(guò)程在程序執(zhí)行時(shí),總是從主函數(shù)main()開(kāi)始,當(dāng)執(zhí)行到語(yǔ)句“m=x()”時(shí)調(diào)用函數(shù)x()。

在函數(shù)x()中執(zhí)行到語(yǔ)句“k=y()”時(shí)調(diào)用函數(shù)y()。

當(dāng)y()函數(shù)執(zhí)行完,就會(huì)返回到其主調(diào)函數(shù)(即x()函數(shù))的調(diào)用位置“k=y()”,并將返回值賦值給變量k,再繼續(xù)執(zhí)行函數(shù)x()中“k=y()”后面的語(yǔ)句。

當(dāng)x()函數(shù)執(zhí)行完,就會(huì)返回到其主調(diào)函數(shù)main()的調(diào)用位置“m=z()”,并將返回值賦值給變量m,再繼續(xù)執(zhí)行main函數(shù)中的“m=x()”后面的語(yǔ)句,直到程序結(jié)束??梢钥闯觯恳粚雍瘮?shù)的調(diào)用均只對(duì)調(diào)用它的函數(shù)是可見(jiàn)的,在函數(shù)返回時(shí),將回到調(diào)用它的語(yǔ)句,然后再執(zhí)行調(diào)用語(yǔ)句后面的其他語(yǔ)句。

2.函數(shù)的遞歸調(diào)用

既然一個(gè)函數(shù)可以調(diào)用另一個(gè)函數(shù),那么這個(gè)函數(shù)能不能調(diào)用自身呢?這個(gè)答案是肯定的,這種調(diào)用自身的情況稱為遞歸調(diào)用。

確切地說(shuō),函數(shù)的遞歸調(diào)用就是指一個(gè)函數(shù)直接或間接調(diào)用自身。前者稱為直接遞歸,后者稱為間接遞歸,如圖8.2所示。

我們將遞歸調(diào)用的函數(shù)稱為遞歸函數(shù)。由于遞歸非常符合人們的思維習(xí)慣,而且許多數(shù)學(xué)的函數(shù)及許多算法或數(shù)據(jù)結(jié)構(gòu)都是遞歸定義的,因此遞歸調(diào)用很有實(shí)用價(jià)值。

圖8.2函數(shù)遞歸調(diào)用從圖8.2可以看出,這兩種遞歸構(gòu)成了循環(huán)調(diào)用,如果沒(méi)有結(jié)束的條件,則會(huì)進(jìn)行無(wú)終止地自身調(diào)用。顯然,在程序中不應(yīng)該出現(xiàn)這種無(wú)終止的遞歸調(diào)用,而只應(yīng)出現(xiàn)有限次數(shù)的、有終止的遞歸調(diào)用,一般可以用if語(yǔ)句來(lái)控制,只有在某一條件成立時(shí)才繼續(xù)執(zhí)行遞歸調(diào)用,否則就不再繼續(xù)。

例8.8

問(wèn)年齡問(wèn)題。

有5個(gè)人坐在一起,問(wèn)第5個(gè)人多少歲,他說(shuō)他比第4個(gè)人大3歲。問(wèn)第4個(gè)人歲數(shù),他說(shuō)他比第3個(gè)人大3歲。問(wèn)第3個(gè)人,又說(shuō)比第2個(gè)人大3歲。問(wèn)第2個(gè)人,說(shuō)比第1個(gè)人大3歲。最后問(wèn)第1個(gè)人,他說(shuō)是10歲。請(qǐng)問(wèn)第5個(gè)人多大?

問(wèn)題分析:從題目可知,要想知道第5個(gè)人的年齡,就必須先知道第4個(gè)人的年齡,而第4個(gè)人的年齡也不知道,只有知道第3個(gè)人年齡才會(huì)知道第4個(gè)人的年齡,而第3個(gè)人的年齡又取決于第2個(gè)人的年齡,第2個(gè)人的年齡取決于第1個(gè)人的年齡。并且每個(gè)人的年齡都比其前一個(gè)人的年齡大3歲。如果用age(1)代表第1個(gè)人的年齡,則age(2)代表第2個(gè)人的年齡,……,age(n)代表第n個(gè)人的年齡。由此,我們可以得到:

age(n)=age(n-1)+3

age(n-1)=age(n-2)+3

age(5)=age(4)+3

age(4)=age(3)+3

age(3)=age(2)+3

age(2)=age(1)+3

age(1)=10

如果用數(shù)學(xué)公式可以表述成:

可以看出,當(dāng)n>1時(shí),求第n個(gè)人年齡的公式是相同的,可以用一個(gè)函數(shù)來(lái)表示上述關(guān)系。圖8.3表示了求第5個(gè)人年齡的過(guò)程。該過(guò)程分為“遞推過(guò)程”和“回代過(guò)程”。在“遞推過(guò)程”過(guò)程中,從要知道的第5個(gè)人的年齡,去求第4個(gè)人的年齡,一直找下去,直到找到一個(gè)人的年齡是確切的值(第1個(gè)人的年齡為10歲)。這第1個(gè)人的年齡就是一個(gè)轉(zhuǎn)折點(diǎn),因此只要“回代”就可知道第2個(gè)人的年齡,再“回代”就可知道第3個(gè)人的年齡,依次遞推,就可求出第5個(gè)人的年齡。

圖8.3遞歸過(guò)程我們可以使用一個(gè)函數(shù)age()來(lái)實(shí)現(xiàn)遞歸過(guò)程。而遞歸的終止條件就是當(dāng)n=1時(shí),age(1)=10。

age(intn)

{intr;

if(n==1)r=10;

elser=age(n-1)+3;

return(r);

}

main()

{printf("第5個(gè)人的年齡為:%d",age(5));}

程序運(yùn)行結(jié)果:

第5個(gè)人的年齡為:22

例8.9

編寫(xiě)n!

的遞歸調(diào)用程序。

n!

的遞歸定義如下:

當(dāng)n>1時(shí),n!=n*(n-1)!,例如:5!=5*4!,4!=4*3!,…

編寫(xiě)的程序如下:

longfunc(intn)

{longr;

if(n==0||n==1)r=1;

elser=n*func(n-1);

return(r);

}

main()

{intn;

printf("Pleaseinputn:");

scanf("%d",&n);

printf("\n%d!=%ld\n",n,func(n));

}根據(jù)本例,讀者可以自己嘗試用遞歸調(diào)用編寫(xiě)程序計(jì)算:s=1+2+3+…+n,求這個(gè)累加時(shí)我們也可以得出數(shù)學(xué)公式:

對(duì)于設(shè)計(jì)遞歸程序,一般可分為兩個(gè)步驟:

(1)確定遞歸終止的條件。

(2)確定將一個(gè)問(wèn)題轉(zhuǎn)化成另一個(gè)問(wèn)題的規(guī)律。即找到前后兩項(xiàng)之間的規(guī)律,例如求n!,前后兩項(xiàng)之間的規(guī)律就是n!=n*(n-1)!。

8.5.1用函數(shù)指針變量調(diào)用函數(shù)

C語(yǔ)言中,可以用指針變量指向整型變量、字符串、數(shù)組,也可以指向一個(gè)函數(shù)。一般來(lái)說(shuō),程序中的每一個(gè)函數(shù)經(jīng)編譯連接后,其目標(biāo)代碼在計(jì)算機(jī)內(nèi)存中是連續(xù)存放的,該代碼的首地址就是函數(shù)執(zhí)行時(shí)的入口地址。函數(shù)名本身就代表該函數(shù)的入口地址。也可以用指針指向這個(gè)入口地址,這樣的指針就稱為函數(shù)的指針。因此,既然可以使用函數(shù)名調(diào)用函數(shù),當(dāng)然也可以用函數(shù)的指針調(diào)用函數(shù)。8.5*函?數(shù)?與?指?針指向函數(shù)的指針變量定義形式如下:

數(shù)據(jù)類型(*指針變量名)();

其中()不能省略。例如:

long(*p)();

說(shuō)明:

(1)“數(shù)據(jù)類型”是指函數(shù)返回值的類型。

(2)函數(shù)的調(diào)用可以通過(guò)函數(shù)名調(diào)用,也可以通過(guò)函數(shù)指針調(diào)用(即用指向函數(shù)的指針變量調(diào)用)。

(3)(*p)()表示定義了一個(gè)指向函數(shù)的指針變量p,它不是固定指向哪一個(gè)函數(shù),而只是表示定義了這樣一個(gè)類型的變量,它是專門(mén)用來(lái)存放函數(shù)入口地址的。在程序中如果將哪一個(gè)函數(shù)的入口地址賦給它,它就指向哪一個(gè)函數(shù)。在一個(gè)程序中,一個(gè)指針變量可以先后指向返回值類型相同的不同函數(shù)。即在程序中,函數(shù)指針變量的值可以改變,但它所指向的函數(shù)返回值類型必須相同。

(4)給函數(shù)指針變量賦值時(shí),只需給出函數(shù)名而不必給出參數(shù),如:

p=func;

(5)用函數(shù)指針變量調(diào)用函數(shù)時(shí),只需將(*p)代替函數(shù)名即可,在(*p)之后的括號(hào)中根據(jù)需要寫(xiě)上實(shí)參。如下面語(yǔ)句表示“調(diào)用由p指向的函數(shù),實(shí)參為n,得到的函數(shù)值輸出”。

printf("\n%d!=%ld\n",n,(*p)(n));

(6)對(duì)函數(shù)指針變量進(jìn)行除指向運(yùn)算之外的運(yùn)算是沒(méi)有意義的。若p為函數(shù)指針變量,則p=p+1、p++、p--運(yùn)算都是沒(méi)有意義的。

例如,如果我們將例8.9的函數(shù)調(diào)用改成使用函數(shù)指針變量調(diào)用,程序可以改成:

longfunc(intn)

{longr;

if(n==0||n==1)r=1;

elser=n*func(n-1);

return(r);

}

main()

{intn;

long(*p)(); /*定義函數(shù)指針變量p,返回值類型是長(zhǎng)整型 */

p=func; /*給函數(shù)指針變量賦值*/

printf("Pleaseinputn:");

scanf("%d",&n);

printf("\n%d!=%ld\n",n,(*p)(n));

}8.5.2用指向函數(shù)的指針作函數(shù)參數(shù)

函數(shù)指針變量經(jīng)常用作函數(shù)參數(shù)傳遞到其他函數(shù)。當(dāng)函數(shù)指針作為某函數(shù)的參數(shù)時(shí),可以實(shí)現(xiàn)將函數(shù)指針?biāo)赶虻暮瘮?shù)入口地址傳遞給該函數(shù)。這種情況下,當(dāng)函數(shù)指針指向不同函數(shù)的入口地址時(shí),在該函數(shù)中就可以調(diào)用不同的函數(shù),且不需要對(duì)該函數(shù)體作任何修改。

到目前為止,函數(shù)的參數(shù)可以是變量、指向變量的指針變量、數(shù)組名、指向數(shù)組的指針變量,還有函數(shù)指針以及下一章要講到的結(jié)構(gòu)體指針。

下面通過(guò)一個(gè)例子介紹函數(shù)指針作為參數(shù)。

例8.10

用復(fù)化梯形公式計(jì)算下列三個(gè)定積分值:

可以使用函數(shù)指針作為函數(shù)參數(shù)來(lái)實(shí)現(xiàn)。

程序如下:

#include<stdio.h>

#include<math.h>

main()

{doublecomputeT(),f1(),f2(),f3(); /*聲明函數(shù)*/

double(*p)(); /*聲明指向函數(shù)的指針變量p*/

p=f1;printf("s1=%f\n",computeT(10,0.0,1.0,p)); /*輸出第1個(gè)積 分值*/

p=f2;printf("s2=%f\n",computeT(10,-1.0,1.0,p)); /*輸出第2個(gè)積 分值*/

p=f3;printf("s3=%f\n",computeT(10,0.0,1.0,p)); /*輸出第3個(gè)積 分值*/

}

doublecomputeT(n,a,b,f) /*梯形法求積分值的函數(shù)*/

intn;

doublea,b,(*f)();

{intk;doubles,h;

h=(b-a)/n;

s=((*f)(a)+(*f)(b))/2;

for(k=1;k<n;k++)s=s+(*f)(a+k*h);

s=s*h;

return(s);

}

doublef1(doublex)

{return(exp(-x*x));}

d

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論