版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、第第3章章 函數和編譯預處理函數和編譯預處理3.1 函數概述函數概述 3.2函數的定義和調用函數的定義和調用 3.3 函數的參數傳遞函數的參數傳遞 3.4 函數的嵌套調用和遞歸調用函數的嵌套調用和遞歸調用 3.5 內置函數內置函數 3.6 變量和函數的屬性變量和函數的屬性 3.7 編譯預處理編譯預處理 3.1 概述概述一個較大的程序不可能完全由一個人從頭至尾地完成,更不可能把所有的內容都放在一個主函數中。為了便于規(guī)劃、組織、編程和調試,一般的做法是把一個大的程序劃分為若干個程序模塊(即程序文件),每一個模塊實現一部分功能。不同的程序模塊可以由不同的人來完成。在程序進行編譯時,以程序模塊為編譯單
2、位,即分別對每一個編譯單位進行編譯。如果發(fā)現錯誤,可以在本程序模塊范圍內查錯并改正。在分別通過編譯后,才進行連接,把各模塊的目標文件以及系統文件連接在一起形成可執(zhí)行文件。在一個程序文件中可以包含若干個函數。無論把一個程序劃分為多少個程序模塊,只能有一個main函數。程序總是從main函數開始執(zhí)行的。在程序運行過程中,由主函數調用其他函數,其他函數也可以互相調用。在c語言中沒有類和對象,在程序模塊中直接定義函數。可以認為,一個c程序是由若干個函數組成的,c語言被認為是面向函數的語言。c+面向過程的程序設計沿用了c語言使用函數的方法。在c+面向對象的程序設計中,主函數以外的函數大多是被封裝在類中的
3、。主函數或其他函數可以通過類對象調用類中的函數。無論是c還是c+,程序中的各項操作基本上都是由函數來實現的,程序編寫者要根據需要編寫一個個函數,每個函數用來實現某一功能。因此,讀者必須掌握函數的概念以及學會設計和使用函數。 “函數”這個名詞是從英文function翻譯過來的,其實function的原意是“功能”。顧名思義,一個函數就是一個功能。在實際應用的程序中,主函數寫得很簡單,它的作用就是調用各個函數,程序各部分的功能全部都是由各函數實現的。主函數相當于總調度,調動各函數依次實現各項功能。開發(fā)商和軟件開發(fā)人員將一些常用的功能模塊編寫成函數,放在函數庫中供公共選用。程序開發(fā)人員要善于利用庫函
4、數,以減少重復編寫程序段的工作量。圖3.是一個程序中函數調用的示意圖。 圖3.main()func1()func2()func3()func5()func4()例31 在主函數中調用其他函數。#include /*ex3_1.cpp*int calc_sum(int n) /定義定義calc_sum()函數函數 int k,s; s=0; for (k=1;k=n;k+) s=s+k; return s; void print_word(void) /定義定義print_word()函數函數 couthello,c+!n; if (n1) coutthe sum is: calc_sum(n)
5、endl; /調用調用calc_sum()函數函數 print_word(); /調用調用rint_word()函數函數 若用戶從鍵盤輸入的數是4, 運行情況如下:the sum is:10hello,c+! 從用戶使用的角度看,函數有兩種:(1) 系統函數,即庫函數。這是由編譯系統提供的,用戶不必自己定義這些函數,可以直接使用它們。(2) 用戶自己定義的函數。用以解決用戶的專門需要。從函數的形式看,函數分兩類:(1) 無參函數。調用函數時不必給出參數。(2) 有參函數。在調用函數時,要給出參數。在主調函數和被調用函數之間有數據傳遞。3.2 函數的定義和調用 3.2.1 定義無參函數的一般形式
6、定義函數的一般形式如下:類型標識符 函數名(形式參數列表) 聲明語句執(zhí)行語句【例3.1】的print_word函數是無參函數,它的定義中首句也可以寫成:void print_word( ) /定義print_word()函數的首部; 或:void print_word(void) /定義print_word()函數的首部【例3.1】的calc_sum函數是有參函數,不過它只有一個參數,參數的類型是整數類型。 int calc_sum(int n) /定義calc_sum()函數 c+要求在定義函數時必須指定函數的類型。下面的函數f則有兩個參數,第一個是整數類型,第二個是單精度實數類型,而函數的
7、返回值是雙精度實數類型:double f(int x, float y) /定義f( )函數的首部 (1)對庫函數的聲明其實,對庫函數的聲明語句已經寫在有關包含文件中了,因此只要在程序文件頭用include語句將這些包含文件包含到本程序中來,就完成了對庫函數的聲明。 (2)對自定義函數的聲明必須在調用某自定義函數的語句之前寫上如下聲明語句:函數類型關鍵字 函數名(參數1類型,參數1名稱,參數2類型,參數2名稱); 3.2.2 函數的聲明(2)對自定義函數的聲明 (續(xù))也可以在聲明語句中略去參數的名稱,或寫一個任意名稱,這叫做函數原型(function prototype),即聲明函數原型。函數
8、原型有下列兩種表示形式:函數類型關鍵字 函數名(參數1類型,參數2類型);函數類型關鍵字 函數名(參數1類型,標識符1,參數2類型,標識符2);c+中的函數原型說明了函數的類型、函數名、函數各形式參數類型。使用函數原型是c和c+的一個重要特點。 3.2.2 函數的聲明【例【例3.2】 對被調函數做聲明的示例。對被調函數做聲明的示例。#include /*ex3_2.cpp*void main() float add(float x,float y); /對對add函數作聲明函數作聲明 float subtract(float,float); /對對subtract函數作聲明,用函數原型函數作聲
9、明,用函數原型 double multiply(float p,float q); /對對multiply函數作聲明,參數名任意函數作聲明,參數名任意 float a,b,c1,c2; double c3; cout ab; c1=add(a,b); c2=subtract(a,b); c3=multiply(a,b); cout c1,c2,c3endl; float add(float x,float y) /定義add函數 float z; z=x+y; return (z);float subtract(float x,float y) /定義subtract函數 float z; z
10、=x-y; return (z);double multiply(float x,float y) /定義multiply函數 double z; z=x*y; return (z);運行情況如下:please input a,b:4.25 2.00 6.25,2.25,8.5 說明:(1)對函數的定義和函數聲明是兩回事,不要混淆。 (2)之所以函數原型中可以省略形式參數的名稱,是因為形式參數的名稱是無關緊要的,且在調用前形參并不存在。 (3)函數聲明語句的位置。函數聲明語句可以放在主調函數中,也可放在函數外面,只要出現在調用語句之前即聲明有效。 3.2.3 函數的返回值(1) 函數的返回值是
11、通過函數中的return語句獲得的。return語句將被調用函數中的一個確定值帶回主調函數中去。return語句后面的括號可以要,也可以不要。return后面的值可以是一個表達式。(2) 函數值的類型。既然函數有返回值,這個值當然應屬于某一個確定的類型,應當在定義函數時指定函數值的類型。(3) 如果函數值的類型和return語句中表達式的值不一致,則以函數類型為準,即函數類型決定返回值的類型。對數值型數據,可以自動進行類型轉換。第(1)種形式是基本的形式。為了便于閱讀程序,也允許在函數原型中加上參數名,就成了第(2)種形式。但編譯系統并不檢查參數名。因此參數名是什么都無所謂。上面程序中的聲明也
12、可以寫成float add(float a,float b); /參數名不用x、y,而用a、b 效果完全相同。應當保證函數原型與函數首部寫法上的一致,即函數類型、函數名、參數個數、參數類型和參數順序必須相同。在函數調用時函數名、實參類型和實參個數應與函數原型一致。說明: (1) 前面已說明,如果被調用函數的定義出現在主調函數之前,可以不必加以聲明。因為編譯系統已經事先知道了已定義的函數類型,會根據函數首部提供的信息對函數的調用作正確性檢查。有經驗的程序編制人員一般都把main函數寫在最前面,這樣對整個程序的結構和作用一目了然,統覽全局,然后再具體了解各函數的細節(jié)。此外,用函數原型來聲明函數,還
13、能減少編寫程序時可能出現的錯誤。由于函數聲明的位置與函數調用語句的位置比較近,因此在寫程序時便于就近參照函數原型來書寫函數調用,不易出錯。所以應養(yǎng)成對所有用到的函數作聲明的習慣。這是保證程序正確性和可讀性的重要環(huán)節(jié)。(2) 函數聲明的位置可以在調用函數所在的函數中,也可以在函數之外。如果函數聲明放在函數的外部,在所有函數定義之前,則在各個主調函數中不必對所調用的函數再作聲明。例如: char letter(char,char); /本行和以下兩行函數聲明在所有函數之前且在函數外部float f(float,float); /因而作用域是整個文件因而作用域是整個文件 int i(float, f
14、loat); int main( ) /在在main函數中不必對它所調用的函數作聲明函數中不必對它所調用的函數作聲明char letter(char c1,char c2) /定義定義letter函數函數float f(float x,float y) /定義定義f函數函數 int i(float j,float k) /定義定義i函數函數如果一個函數被多個函數所調用,用這種方法比較好,不必在每個主調函數中重復聲明。函數調用的一般形式:函數名(實際參數列表) 如果是調用無參函數,則“實參表列”可以沒有,但括號不能省略。如果實參表列包含多個實參,則各參數間用逗號隔開。實參與形參的個數應相等,類型
15、應匹配(相同或賦值兼容)。實參與形參按順序對應,一對一地傳遞數據。但應說明,如果實參表列包括多個實參,對實參求值的順序并不是確定的。3.2.4函數的調用按函數在語句中的作用來分,可以有以下3種函數調用方式:. 函數語句把函數調用單獨作為一個語句,并不要求函數帶回一個值,只是要求函數完成一定的操作。如【例3.1】中的print_word()函數調用語句。 2. 函數表達式函數出現在一個表達式中,這時要求函數帶回一個確定的值以參加表達式的運算。如c=2*max(a,b);3. 函數參數函數調用作為一個函數的實參。如m=max(a,max(b,c); /max(b,c)是函數調用,其值作為外層max
16、函數調用的一個實參函數調用的方式3.3 函數的參數傳遞 形參:在定義函數時函數名后面括號中的變量名稱為形式參數(formal parameter),簡稱形參。形參是無內存單元(因而不存在)的任何合法標識符。實參:在調用一個函數時,調用語句的函數名后面括號中的參數稱為實際參數(actual parameter),簡稱實參。實參是實際存在(因而有特定值)的常量、變量或表達式。 【例【例3.3】 形參和實參及其數據傳遞。形參和實參及其數據傳遞。#include /*ex3_3.cpp* using namespace std;float volume(float r,float h) /定義有參函數
17、定義有參函數volume求圓柱體體積,求圓柱體體積,r和和h是形參是形參 float v;v=3.14*r*r*h; return (v);void main()float a,b,c;cout a b;c=volume(a,b); /調用函數調用函數volume,a和和b是實參。函數值賦給變量是實參。函數值賦給變量ccout the volume is cendl; 運行情況如下:please input two float nukbers:2.0 5.0 the volume is 62.8有關形參與實參的說明:(1) 在定義函數時指定的形參,在未出現函數調用時,它們并不占內存中的存儲單元
18、,因此稱它們是形式參數或虛擬參數,表示它們并不是實際存在的數據,只有在發(fā)生函數調用時,函數max中的形參才被分配內存單元,以便接收從實參傳來的數據。在調用結束后,形參所占的內存單元也被釋放。(2) 實參可以是常量、變量或表達式,如max(3, a+b);但要求a和b有確定的值。以便在調用函數時將實參的值賦給形參。(3)在定義函數時,必須在函數首部指定形參的類型,至于形參使用何名字可隨意。 (4)實參與形參的類型應相同或賦值兼容。兩種參數類型完全一致無疑是完全合法、正確的。如果實參為整型而形參為實型,或者相反,則按不同類型數值的賦值規(guī)則進行轉換,原則上不出現語法錯誤,但結果可能帶來某些非期望的誤
19、差。例如實參a的值是3.5,而被調函數的形參x為整型,則調用該函數時會將3.5轉化為整數3,然后送到形參x,故x得到的是3,由3計算出的函數值與人們期望由3.5計算出的函數值一般是有差別的。但字符型與整型可以互相通用。 3.3.2 參數的值傳遞 值傳遞參數的實現是系統將實參拷貝一個副本給形參,拷貝后兩者就斷開關系。在被調函數中,形參可以被改變,但這只影響副本中的形參值,而不影響調用函數的實參值。所以這類函數有對原始數據保護的作用。換一句話說,這種參數傳遞機制是單向影響,即只能由實參將值傳給形參(實參影響形參);而形參在函數中的值如果發(fā)生修改,不會反過來影響與之對應的實參。 【例3.4】 參數值
20、傳遞的演示。#include /*ex3_4.cpp* using namespace std;int max(int x,int y) /定義有參函數max求兩數最大值,x和y是形參 float m;coutthe initial x,y:x,yy?x:y;x=2*x;y=y+1;coutthe new x,y:x,yendl;return (m);void main()float a,b,c;cout a b;c=max(a,b); /調用函數max,a和b是實參,函數值賦給變量c。cout the maxinum is: cendl;cout after calling fuction,
21、a,b:a,bendl;運行情況如下:please input two integer numbers:3 8 the initial x,y:3,8the new x,y:6 9the maxinum is:8after calling fuction,a,b:3,83.3.3 參數的地址傳遞除了3.3.2小節(jié)介紹的值傳遞參數方式外,函數調用還有一種特殊的值傳遞形式,即傳遞的值不是一般的數值,而是一些內存單元地址編號(即地址),這時,一般稱之為參數的地址傳遞。在這種參數傳遞形式中,無論在函數的定義中出現的形參還是在調用語句中出現的實參,都是代表一些內存單元地址編號(即地址數值),而不是一般的
22、數值。c+中的參數地址傳遞情況一般有如下幾種:實參可以是一個有確定值的普通變量的地址,或者是一個已經初始化的指針變量;或者是一個初始化的數組名;或者是一個具體的函數名。而形參可以是一個任意普通變量的地址,或是一個任意指針變量,或是一個任意的數組名,或是一個指向函數的指針變量(對應于實參是具體函數名)。 實際上,這種參數傳遞機制就是在函數調用時把一個內存單元地址傳遞給形參,使形參也具有實參的內存單元地址(即兩者對應同一個內存單元),稱作形參和實參地址結合,兩者合二為一。這樣一來,任何時候形參的值等于實參的值;而實參的值也等于形參的值。因此,形參在函數中發(fā)生變化后,也會引起實參跟著變化(因為它們是
23、捆綁在一起的,一體化的)。這就意味著按地址傳遞的方式,在調用剛開始時實參的值影響了形參;而在被調函數執(zhí)行過程中形參值若發(fā)生了變化,它也會影響實參的值變化。即機制是雙向影響,這與普通值傳遞方式的單向影響機制形成對比。3.3.4 帶默認值的參數c+語言中,允許在函數聲明或定義時給一個或多個參數指定默認值。通語言中,允許在函數聲明或定義時給一個或多個參數指定默認值。通常調用函數時,要為函數的每個形式參數給定相應的值。例如下面的常調用函數時,要為函數的每個形式參數給定相應的值。例如下面的delay( )函數作用是作時間延遲,在沒有使用默認值參數的情況下,是按函數作用是作時間延遲,在沒有使用默認值參數的
24、情況下,是按如下普通方式聲明和定義的:如下普通方式聲明和定義的: 【例【例3.5】 延遲函數的使用。延遲函數的使用。#include /*ex3_5.cpp* void delay(int loop); /函數聲明函數聲明void main( ) coutbeginendl; delay(1000); /函數調用函數調用 coutendendl;void delay(int loop) /函數定義函數定義 if (loop=0) return; for(int i=0;iloop;i+) coutiendl; /輸出輸出i的值為了清楚看到程序執(zhí)行的情況的值為了清楚看到程序執(zhí)行的情況 如果每次調
25、用延遲時間基本一樣,可以使用c+中默認值參數函數形式來解決問題。解決的方法就是在函數的聲明(或定義)時給定默認值即可。具體做法只要把delay()函數的聲明改為下列形式: void delay(int loop=1000); /指定參數默認值為1000以后如果需要延遲相同時間1000,都可以不必指定實參的值而直接調用函數:delay(); /不給定實參,形參將得到默認值1000 delay(500); /給定實參,形參將得到所給的值500 如果有多個形參,可以使每個形參有一個默認值;也可以只對一部分形參指定默認值。如前面的求圓柱體體積的函數volume,可以這樣聲明:float volume(
26、float r,float h=8.5); /只對形參h指定默認值8.5這時函數調用可采用以下形式:volume(6.0); /相當于volume(6.0,8.5)volume(6.0,7.2); /r的值為6.0,h的值為7.2 c+中實參和形參的結合是從左至右進行的,第1個實參必然與第1個形參結合,第2個實參必然與第2個形參結合,。因此,指定默認值的參數必須放在參數列表中的最右邊。3.4 函數的嵌套調用和遞歸調用 c+不允許對函數作嵌套定義,也就是說在一個函數中不能完整地包含另一個函數。在一個程序中每一個函數的定義都是互相平行和獨立的。雖然c+不能嵌套定義函數,但可以嵌套調用函數,也就是說
27、,在調用一個函數的過程中,又調用另一個函數。見圖3.2示意。 圖3.2 在程序中實現函數嵌套調用時,需要注意的是: 在調用函數之前,需要對每一個被調用的函數作聲明(除非定義在前,調用在后)?!纠?.6】編程求組合,其中求組合的功能要求用函數完成。分析:根據組合的計算公式,知組合函數有兩個形參:m和n,可以用自定義函數comb(int n,int m)表示求組合。而在comb函數中需要3次計算階乘,如果定義函數fac(k)求k的階乘,然后在comb函數中調用fac函數,可以使程序代碼簡單,只要在comb函數中寫一個語句“c=fac(m) / (fac(n)*fac(m-n) );”即可求出組合值
28、?!纠?.6】 程序代碼如下:#include /*ex3_6.cpp*using namespace std;long fac(int k) / 定義求階乘的函數long f=1; int i; for(i=1;i=k;i+) f = f*i; return f;long comb(int n, int m) /定義組合函數long c; c=fac(m)/(fac(n)*fac(m-n);/ 嵌套調用階乘函數 return c;void main() int n,m; long c;coutplease input two integer numbers:m,nmn;c=comb(n,m)
29、; / 調用組合函數combcoutc=c0,可用n*fac(n-1)表示,即fac(n)的函數體內將遞歸調用fac()本身;但一旦參數n為0時,則終止調用函數自身并給出函數值1。程序如下: 101 !0nnn( n)n! =using namespace std;#include /*ex3_7.cpp*long fac(int n)long f; if (n=0) f=1; else f=n*fac(n-1); / 遞歸調用,求(n-1)! return f;void main( )long y; int n; coutplease input a integer n n; y=fac(n
30、); /調用fac(n)求n!coutn=n,y=yendl;運行時,如果輸入:3, 運行結果如下:n=3, y=6遞歸調用及返回過程如圖3.5所示,圖中的數字序號表示遞歸調用和返回的先后順序。 圖3.5 求3!的遞歸過程 fac(3 )fac( )n=33*fac(2)return 6main( )fac( )n=22*fac(1)return 2n=0fac(0)=1return 1n1*fac(0) return 1fac( )fac( )從求n!的遞歸程序中可以看出,遞歸定義有兩個要素:(1) 遞歸終止條件。也就是所描述問題的最簡單情況,它本身不再使用遞歸的定義,即程序必須終止。如上例
31、,當n=0時,fac(n)=1,不再使用f(n-1)來定義。(2) 遞歸定義使問題向終止條件轉化的規(guī)則。遞歸定義必須能使問題越來越簡單,即參數越來越接近終止條件的參數;達到終止條件參數時函數有確定的值。如上例,f(n)由f(n-1)定義,越來越靠近f(0),即參數越來越接近終止條件參數0;達到終止條件參數時函數有確定的值是f(0)=1。 【例3.8】 漢諾塔問題 漢諾塔(tower of hanoi)問題據說來源于布拉瑪神廟。該問題的裝置如圖3.6所示(圖上僅畫三個金片以簡化問題的原理,原問題有64個金片),底座上有三根金鋼石的針,第一根針a上放著從大到小64個金片。解決該問題就是要想法把所有
32、金片從第一根針a上移到第三根針c上,第二根針b作為中間過渡。要求是每次只能移動一個金片,并且任何時候不允許大的金片壓在小的金片上面。 圖3.6 三個金片的漢諾塔問題裝置 abc1. 本問題的遞歸終止條件。如果只有1個盤,顯然問題的解就很明顯是:直接把金片從a移到c。因此終止條件是n = 1;終止條件對應的操作是直接把金片從a移到c,示意ac。2. 本問題的遞歸分析:移動n個金片從a到c,必須先將n-1個金片從a借助c移動到b,移動n-1個金片與原問題相同,但規(guī)模變小,即向終止條件接近,因此,此問題可以用遞歸過程完成。遞歸過程可以用如下步驟表示:(1)將n-1個金片從a經過c移動到b。(2)將第
33、n個金片從a直接移動到c。(3)再將n-1個金片從b經過a移動到c。 一般地,設將n個金片從x針借助y針移動到z針的函數原形為:void hanoi(int n,char x,char y,char z)根據解題步驟,可以寫出求解n個金片的漢諾塔函數如下:#include /*ex3_8.cpp*using namespace std;void hanoi(int n,char x,char y,char z) if (n=1) /n=1時,直接將金片從x移動到z cout x z 1時 hanoi(n-1,x,z,y); /先將n-1個金片從借助z移動到y cout x z 1時,就遞歸調用
34、hanoi(),每次n減1。最后當n=1時,直接移動該金片就可以了。 主函數如下: void main() int n; cout input n: n; hanoi(n,a,b,c); / n個金片從a針借助b針移動到c針 雖然遞歸調用在寫程序時很簡單,但執(zhí)行起來卻很復雜(時間、存儲空間都開銷大)。對于漢諾塔問題程序的執(zhí)行過程分析比較復雜,有興趣的讀者可參閱教材對3個盤情景的分析(圖3.7 及其相應文字敘述)。 調用函數時需要一定的時間和空間的開銷。下圖表示的是一般函數調用的過程。一般函數調用過程3.5 內置函數3.5.1 內置函數的作用內置函數也稱內聯函數、內嵌函數。引入內置函數的目的是為
35、了提高程序中函數調用的效率。c+提供一種提高效率的方法,即在編譯時將所調用函數的代碼直接嵌入到主調函數中,而不是將流程轉出去。這種嵌入到主調函數中的函數稱為內置函數(inline function),又稱內嵌函數。在有些書中把它譯成內聯函數。指定內置函數的方法很簡單,只需在函數首行的左端加一個關鍵字inline即可。【例3.9】 判斷用戶從鍵盤輸入的系列字符是數字字符還是其它字符的函數is_number()。#include /*ex3_9a.cpp*int is_number(char); /函數聲明void main( ) char c; while (c=cin.get()!=n) if
36、 (is_number(c) /調用一個小函數coutyou enter a digit n;else cout=0& ch=9)?1:0; 程序中不斷到設備中讀取數據,頻繁調用程序中不斷到設備中讀取數據,頻繁調用is_number()函數。為了避免頻函數。為了避免頻繁調用函數,提高執(zhí)行效率,可以將【例繁調用函數,提高執(zhí)行效率,可以將【例3.9】程序改為:】程序改為:#include /*ex3_9b.cpp*void main() char c; while (c=cin.get()!=n) if (c=0& c=9)?1:0) /修改處:直接計算表達式修改處:直接計算表達式
37、 coutyou enter a digit n; else coutyou enter a non-digit n; 修改后的程序在if語句中用表達式替換了函數調用。在程序運行上,提高了一些執(zhí)行效率,因為免去了大量的函數調用開銷。但是,由于is_number函數比相應的表達式可讀性好,所以修改后的代碼可讀性降低,尤其若程序中多處出現is_number的替換時,會大大降低可讀性。我們希望既要用函數調用來體現其結構化和可讀性,又要使效率盡可能地高。為了盡量做到兩全其美,c+中引入內置函數這個方法。3.5.2 定義和使用內置函數內置函數的定義格式如下: inline 函數名(形參列表) /函數體
38、內置函數的聲明格式如下:inline 函數名(形參類型表);其實,內置函數只要在開頭一次性聲明為inline即可,而后面的函數定義仍可寫成一般函數定義的形式,編譯器也會將函數視為內置函數。對【例3.9】使用內置函數來解決,代碼可以寫成下列形式:#include /*ex3_9c.cpp*inline int is_number(char);/inline函數聲明函數聲明void main( ) char c; while (c=cin.get()!=n) if (is_number(c) /調用一個小函數調用一個小函數coutyou enter a digit n;else cout=0&am
39、p; ch=9)?1:0; 關于內置函數的說明:(1)內置函數與一般函數的區(qū)別在于函數調用的處理。一般函數進行調用時,要將程序執(zhí)行到被調用函數中,然后返回到主調函數中;而內置函數在調用時,是將調用部分用內置函數體來替換。(2)內置函數必須先聲明后調用。因為程序編譯時要對內置函數替換,所以在內置函數調用之前必須聲明是內置的(inline),否則將會像一般函數那樣產生調用而不是進行替換操作。(3)在內置函數中,不能含有復雜的結構控制語句,如switch、for和while。如果內置函數有這些語句,則編譯器將該函數視同一般函數那樣產生函數調用。(4)遞歸函數不能用作內置函數。(5)以后講到的類中,所
40、有定義在說明內部的函數都是內置函數。3.6.1 變量的作用域 首先將變量按作用域大小分類,可以分為局部變量和全局變量。c+中并不是所有的變量對任何函數都是可知的。一些變量在整個程序或文件中都是可見的,這些變量作用域大,被稱為全局變量。一些變量只能在一個函數或塊中可見,這些變量作用域小,被稱為局部變量。變量作用域的大小和它們的存儲區(qū)域有關。全局變量存儲在全局數據區(qū)(也稱為靜態(tài)存儲區(qū)),它在程序運行的時候被分配存儲空間,當程序結束時釋放存儲空間。局部變量存儲在堆棧數據區(qū),當程序執(zhí)行到該變量聲明的函數(或程序塊)時才開辟存儲空間,當函數(或程序塊)執(zhí)行完畢時釋放存儲空間。而決定變量作用域大小的根源是
41、它的定義位置。3.6 變量和函數的屬性1局部變量局部變量是指定義在函數或程序塊內的變量,它們的作用域分別在所定義的函數體或程序塊內。下面是一些局部變量的實例:void main()int x ; /x為函數級的局部變量。其作用域為main()函數內 int y ; /y為語句塊級的局部變量,其作用域為該塊內 void fun( ) char ch ; /ch為函數級局部變量,其作用域為fun()函數內 【例3.10】 局部變量的使用。#include /*ex3_10.cpp* double fun1(double a, double b) /fun1函數中3個局部變量a、b、c double
42、 c; c=a+b; return c; double fun2(double a,double b) /fun2函數中3個局部變量a、b、c double c; c=a*b; return c;void main()/main函數中3個局部變量a、b、cdouble a,b,c; coutab; c=fun1(a,b); couta+b=cendl; c=fun2(a,b); couta*b=cendl;程序運行結果如下:input two numbers:3.5 4 a+b=7.5 a*b=14注意:不同范圍的局部變量可以同名,但同一范圍內不允許同名變量出現(否則有沖突);上例每個函數中都
43、定義了相同名稱的局部變量,比如有3個a;但這個范圍中定義的a與另一個范圍中定義的a并不是同一個對象,即“此a非彼a”。全局變量是定義在函數以外的變量(一般的全局變量也稱外部變量,而一類特殊的全局變量則叫靜態(tài)全局變量),它們原則上對整個文件的函數都是可見的,甚至對本程序的其它文件的函數也可見;但通常條件下的全局變量作用域是從定義變量的位置到該文件的結束。 2全局變量【例3.11】 全局變量的使用#include /*ex3_11.cpp* int a; /a的作用域為整個文件void fun1(); /聲明fun1函數void main() coutaendl; /main函數中使用了全局變量a
44、fun1(); /調用fun1函數coutaendl; /main函數中再次使用全局變量avoid fun1() a=5; / fun1函數中使用全局變量a 程序運行結果如下:05 說明:(1)上例中,在主函數中第一次輸出變量a時,a還沒有賦值,但是執(zhí)行結果顯示0。 (2)全局變量可以定義在任何位置,但其作用域是從定義的位置開始到文件的末尾。而定義在文件中間的全局變量就只能被其下面的函數所使用,全局變量定義之前的所有函數不會知道該變量。 (3)全局變量為函數間數據的傳遞提供了通道。由于全局變量可以被其定義后的函數所使用,所以可以使用全局變量進行函數間數據的傳遞。而且這種傳遞數據的方法可以傳遞多
45、個數據的值?!纠纠?.12】 分別寫兩個函數求給定兩個數的最大公約數和最小公倍數。其分別寫兩個函數求給定兩個數的最大公約數和最小公倍數。其中,要求用全局變量存放最大公約數和最小公倍數。中,要求用全局變量存放最大公約數和最小公倍數。分析:由于求最小公倍數要依賴于最大公約數,所以應先求最大公約數。分析:由于求最小公倍數要依賴于最大公約數,所以應先求最大公約數。故應將求最大公約數的函數寫在前面,求最小公倍數的函數放在后面。故應將求最大公約數的函數寫在前面,求最小公倍數的函數放在后面。#include /*ex3_12.cpp* int greatest_common_divisor(int,int
46、);/聲明求最大公約數的函數聲明求最大公約數的函數int lease_common_multiple(int,int); /聲明求最小公倍數的函數聲明求最小公倍數的函數int max , min; /全局變量分別存放最大公約數、最小公倍數全局變量分別存放最大公約數、最小公倍數void main( ) int a; int b; coutab; greatest_common_divisor(a,b); lease_common_multiple(a,b); cout the greatest common divisor is :maxendl; /使用全局變量使用全局變量max cout t
47、he lease common multiple is :minendl; /使用全局變量使用全局變量minint greatest_common_divisor(int x,int y) int t;int r; if (xy) t=x;x=y;y=t; r=x%y; while(r!=0) x=y; y=r; r=x%y; max=y; return max;/使用全局變量使用全局變量maxint lease_common_multiple(int x,int y) min=x*y/max; /使用全局變量使用全局變量max求全局變量求全局變量min, return min; /返回全局變
48、量返回全局變量min 程序運行結果如下: input a,b:36 15 the greatest common divisor is :3the lease common multiple is :180本例中利用全局變量max和min存儲最大公約數和最小公倍數。最大公約數是在函數greatest_common_divisor( )中賦給max的;最小公倍數是在函數lease_common_multiple()中賦給min的,而max和min又在main()函數中使用。本程序的整個過程就是利用全局變量max和min由greatest_common_divisor()函數和lease_comm
49、on_multiple()函數向main()函數傳遞數據實現的。(4)其它文件也可以使用文件外定義的全局變量,但要求該變量是外部變量類型的全局變量,而且要求在使用該變量的文件中要有對該變量的聲明。外部變量跨文件使用的例子,請參考本章后面的【例3.16】。(5)全局變量降低了函數的通用性,建議不在必要時不要使用全局變量。因為如果函數在執(zhí)行的時候使用了全局變量,那么其他程序使用該函數時也必須將該全局變量一起移過去。另外,全局變量在程序執(zhí)行的全部過程都占用存儲空間,而不是需要時才開辟存儲空間。 3重名局部變量和全局變量作用域規(guī)則在在c+中,雖然不允許相同作用域的變量同名,中,雖然不允許相同作用域的變
50、量同名,但允許不同作用域的變量同名。但允許不同作用域的變量同名。那么在一個特定的位置引用到某個有重名的變那么在一個特定的位置引用到某個有重名的變量時,到底是使用哪個變量呢?量時,到底是使用哪個變量呢?這由如下這由如下重名變量作用域規(guī)則重名變量作用域規(guī)則決定:在某個作決定:在某個作用域范圍內定義的變量在該范圍的子范圍內可用域范圍內定義的變量在該范圍的子范圍內可以定義重名變量,這時原定義的變量在子范圍以定義重名變量,這時原定義的變量在子范圍內是不可見的,但是它還存在,只是在子范圍內是不可見的,但是它還存在,只是在子范圍內由于出現了重名的變量而被暫時隱藏起來,內由于出現了重名的變量而被暫時隱藏起來,
51、過了子范圍后,它又是可見的。過了子范圍后,它又是可見的。 【例3.13】 函數中變量和程序塊中變量重名#include /*ex3_13.cpp*void main( ) int a=1,b=2,c=3; couta,b,cendl; int b=4; couta,b,cendl; a=b; int c; c=b; couta,b,cendl; couta,b,cendl; couta,b,cendl; 程序運行結果如下:1,2,31,4,34,4,44,4,34,2,3 【例3.14】 全局變量和局部變量重名#include /*ex3_14.cpp* void fun1( ); float
52、 x=3.5; void main( ) float x; coutx; coutx in main is :xendl; fun1( ); coutx in main is :xendl; void fun1() coutx in fun1 is :xendl; 運行結果:input a data:6.6 x in main is :6.6x in fun1 is :3.5x in main is :6.6 從變量的空間屬性考慮變量有作用域的概念。那么,從變量的時間屬性考慮變量還有存儲期(storage duration,也稱生命期)的概念,存儲期即變量值在內存中存在的時間。而變量的存儲期是
53、由變量的存儲類別決定的,有兩種存儲類別:動態(tài)存儲方式與靜態(tài)存儲方式。 3.6.2 變量的生存期 1短生存期變量動態(tài)存儲方式所謂動態(tài)存儲方式,是指在程序運行期間動態(tài)地分配存儲空間給變量的方式。這類變量存儲在動態(tài)存儲空間(堆或棧),執(zhí)行其所在函數或程序塊時開辟存儲空間,函數或程序塊結束時釋放存儲空間,生存期為函數或程序塊的運行期間,主要有:函數的形參和函數或程序塊中定義的局部變量(未用static聲明)。使用動態(tài)存儲方式的變量有兩種:自動變量和寄存器類變量。 (1)自動變量:函數中的局部變量默認是自動變量,存儲在動態(tài)數據存儲區(qū)。自動變量可以用關鍵字auto作為存儲類別的聲明。自動變量的生存期為函數
54、或程序塊執(zhí)行期間,作用域也是其所在函數或程序塊。例如:int fun() auto int a; /a為自動類變量 實際編程過程中,關鍵字“auto”可以省略。例如上述自動變量也可聲明為下面形式:int fun() int a; (2)寄存器變量:寄存器變量也是動態(tài)變量,可以用register作為存儲類別的聲明。寄存器變量存儲在cpu的通用寄存器中,因此訪問效率高。寄存器變量的生存期和作用域為其定義所在的函數或程序塊。一般情況下,將局部最常用到的變量聲明為寄存器變量,如循環(huán)變量。下面main()函數中的循環(huán)變量i就使用了寄存器變量:#include void main() register i
55、nt i; int sum=0; for(i=1;i=100;i+) sum+=i; cout1+2+.+100=sumendl;程序運行結果如下:1+2+.+100=5050寄存器變量的使用應注意以下問題:(1)寄存器變量不宜定義過多。計算機中寄存器數量是有限的,不能允許所有的變量都為寄存器變量。如果寄存器變量過多或通用寄存器被其他數據使用,那么系統將自動把寄存器變量轉換成自動變量。(2)寄存器變量的數據長度與通用寄存器的長度相當。一般是char型和int型變量。 2長生存期變量靜態(tài)存儲方式所謂靜態(tài)存儲方式,是指在程序運行期間分配固定的存儲空間給變量的方式。這類變量存儲在全局數據區(qū),當程序運
56、行時開辟存儲空間,程序結束時釋放存儲空間,生存期為程序運行期間。采用靜態(tài)存儲方式的變量有:全局變量(含外部變量、靜態(tài)全局變量)和靜態(tài)局部變量。(1)外部變量:外部變量就是只用數據類型關鍵字而未用static關鍵字定義的全局變量。存儲在全局數據區(qū),生存期為程序執(zhí)行期間。如果不對外部變量另加聲明,則它的作用域是從定義點到所在文件的末尾。其實,外部變量不管在何處定義,可以通過用extern關鍵詞加以聲明后將其作用域擴展到整個程序,聲明語句格式為:extern 外部變量名; 這種擴展作用域的聲明語句有兩種情況用到:對文件內容后面位置所定義的外部變量x,將其作用域擴展到本文件前面的函數。這時只要在該文件
57、的前面用extern對該變量x聲明即可,這叫做提前引用聲明。對本程序的另一個源文件b中定義的外部變量y,要想擴展到本文件a中使用,這時只要在本文件a的前面用extern對該變量y聲明即可,不妨稱此為跨文件引用聲明。 【例3.15】 對定義在同一文件中外部變量,作提前引用聲明以擴展其使用范圍到文件前面。#include /*ex3_15.cpp* extern x; /提前引用聲明void main() x=4; coutxendl;int x; /外部變量x的定義 【例3.16 】對定義在另一文件中的外部變量,作跨文件引用聲明以擴展其作用域到本文件。文件file1.cpp的內容:#includ
58、e /*ex3_16.cpp*extern w; /跨文件引用聲明void main() coutwendl; /使用file2.cpp文件中定義的變量w 文件file2.cpp的內容:int w=10; /外部變量w的定義int fun(int x,int y)return (x+y);程序運行結果如下:10 (2)靜態(tài)變量:靜態(tài)變量存儲在全局數據區(qū),使用static聲明。靜態(tài)變量有兩種:靜態(tài)局部變量和靜態(tài)全局變量。 靜態(tài)局部變量是在定義局部變量時開頭再添加一個static關鍵字所定義的。靜態(tài)局部變量的特點是:程序執(zhí)行時,為其開辟存儲空間直到程序結束,但只能被其定義所在的函數或程序塊所使用。
59、所以靜態(tài)局部變量的生命周期為程序執(zhí)行期間,作用域為其定義所在的函數或程序塊內。如果沒有定義靜態(tài)局部變量的初始值,系統將自動初始化為0。 【例3.17】 靜態(tài)局部變量的使用#include /*ex3_17.cpp* void fun();void main() int i; for(i=0;i3;i+) fun(); void fun()int a=0; static int b=0; /定義靜態(tài)局部變量定義靜態(tài)局部變量 a=a+1; b=b+1; couta,bendl;程序運行結果如下:1,11,21,3 靜態(tài)全局變量是在定義全局變量時開頭再添加一個static關鍵字所定義的。靜態(tài)全局變量
60、的特點是:程序執(zhí)行時,為其在全局數據區(qū)開辟存儲空間并初始化為0,直存儲期仍與外部變量一樣一直延續(xù)到程序結束。但其作用域受限于只能被其定義所在的文件使用,而不能借助前面所講的跨文件引用聲明擴展到文件外?!纠?.18】 靜態(tài)全局變量的演示。文件file1.cpp的內容:#include /*ex3_18.cpp*static int u=10; /定義靜態(tài)全局變量 void fun() cout”this is file1” 文件file2.cpp的內容:#include extern u; /試圖對u作跨文件引用聲明,此時行不通void main() coutuendl; /出現“變量u未定義”錯誤【例3.18】中,
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年個人樓房出租合同標準版本(三篇)
- 2025年中年人自愿離婚協議范文(2篇)
- 2025年產品加工項目合作協議(三篇)
- 2025年個人果園承包合同參考模板(三篇)
- 2025年代理合同協議范例(三篇)
- 2025年二手叉車轉讓協議(三篇)
- 2025年個人文件保密協議(三篇)
- 專題01 空間幾何體的外接球與內切球問題(典型題型歸類訓練)解析版
- 2025年人事外包勞動合同(2篇)
- 造型模板護岸施工方案
- 北京地鐵13號線
- 塑料成型模具設計(第2版)江昌勇課件1-塑料概述
- 產業(yè)園EPC總承包工程項目施工組織設計
- 方形補償器計算
- 為加入燒火佬協會致辭(7篇)
- 兒科重癥監(jiān)護病房管理演示文稿
- 甲基異丁基甲酮化學品安全技術說明書
- 條形基礎的平法識圖課件
- 秘書實務完整版課件全套ppt教程
- 新版神經系統疾病的病史采集和體格檢查ppt
- 義務教育《歷史》課程標準(2022年版)
評論
0/150
提交評論