版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、第4章函數(shù),返回總目錄,目錄,4.1函數(shù)調(diào)用過程,4.2函數(shù)的定義,4.3遞歸函數(shù),4.6習(xí)題四,4.4存儲類型、生存期和作用域,4.5編譯預(yù)處理,返回總目錄,基本要求: )熟悉函數(shù)的定義、聲明與調(diào)用,并熟悉遞歸函數(shù)的定義與使用; )熟悉存儲類型、生存期和作用域; )熟悉編譯預(yù)處理。 學(xué)習(xí)重點(diǎn): )函數(shù)的定義與調(diào)用; )生存期、作用域、編譯預(yù)處理。,4.1函數(shù)調(diào)用過程,4.1.1 函數(shù)調(diào)用的定義,主函數(shù)main( )只能被系統(tǒng)調(diào)用,不能被其它函數(shù)所調(diào)用; 主函數(shù)main( )可以調(diào)用庫函數(shù)或其他函數(shù); 除主函數(shù)main( )外,其他函數(shù)之間可以相互調(diào)用; 在一個程序中,通過調(diào)用關(guān)系將各函數(shù)聯(lián)系
2、在一起,程序總是從main( )函數(shù)開始執(zhí)行,調(diào)用所需要的函數(shù),完成所調(diào)用函數(shù)的功能,返回到main( )函數(shù)繼續(xù)執(zhí)行,最后在main( )函數(shù)中結(jié)束。,函數(shù)調(diào)用:在一個函數(shù)中引用另一個函數(shù),稱為,返回目錄,4.1函數(shù)調(diào)用過程,4.1.2 函數(shù)調(diào)用過程,函數(shù)調(diào)用的圖示,設(shè)有main函數(shù)、fun函數(shù),則它們的調(diào)用過程如圖所示。,4.1函數(shù)調(diào)用過程,4.1.2 函數(shù)調(diào)用過程,函數(shù)調(diào)用的圖示,main() int a,b,c; scanf(%d,%d, ,4.1函數(shù)調(diào)用過程,4.1.2 函數(shù)調(diào)用過程,首先為被調(diào)函數(shù)的所有形式參數(shù)分配內(nèi)存,再計算實際參數(shù)的值,再一一對應(yīng)地賦給相應(yīng)的形式參數(shù)(對于無參函
3、數(shù),不做此工作); 然后進(jìn)入被調(diào)函數(shù)的函數(shù)體,為函數(shù)說明部分定義的變量分配存儲空間,再依次執(zhí)行函數(shù)體中的可執(zhí)行語句; 當(dāng)執(zhí)行到“return”語句時,計算返回值(如果是無返回值的函數(shù),不做這項工作);釋放本函數(shù)中定義的變量所占用的存儲空間(對于static類型變量,其空間不釋放),返回主調(diào)函數(shù)繼續(xù)執(zhí)行。,函數(shù)調(diào)用過程簡述,4.1函數(shù)調(diào)用過程,4.1.2 函數(shù)調(diào)用過程,函數(shù)調(diào)用的規(guī)則說明,被調(diào)用的函數(shù)必須是已經(jīng)存在的函數(shù)(即是庫函數(shù)或用戶自己定義的函數(shù)); 如果調(diào)用庫函數(shù),需要在程序的開頭包含相應(yīng)的頭文件,如使用數(shù)學(xué)庫中的函數(shù),就用#include; 函數(shù)的聲明:詳見4.2.3小節(jié)。,4.1函數(shù)
4、調(diào)用過程,4.1.2 函數(shù)調(diào)用過程,#include int max(int x,int y) int z; if(xy) z=x; else z=y; return z; main( ) int a,b,c; scanf(“%d,%d”, ,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),函數(shù)類型說明,(2) 函數(shù)有1個返回值 這時在被調(diào)用函數(shù)的函數(shù)體中有return語句。此時函數(shù)類型的定義應(yīng)根據(jù)return語句后的表達(dá)式的數(shù)據(jù)類型來定義。例如: int max(int a,int b) return ab?a:b; ,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),函數(shù)類型說明,(2) 函數(shù)有
5、1個返回值 當(dāng)函數(shù)類型與return語句后的表達(dá)式的數(shù)據(jù)類型不一致時,函數(shù)類型決定return語句后的表達(dá)式的數(shù)據(jù)類型,系統(tǒng)自動將表達(dá)式的數(shù)據(jù)類型轉(zhuǎn)換成函數(shù)定義的數(shù)據(jù)類型,在函數(shù)定義過程中,最好定義兩者一致,以免結(jié)果出錯; int max(float a,float b) return ab?a:b; ,main( ) printf(%d,max(12.3,20.6); ,輸出結(jié)果:20,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),函數(shù)類型說明,(2) 函數(shù)有1個返回值 當(dāng)缺省函數(shù)類型定義時,系統(tǒng)默認(rèn)函數(shù)類型為int或char,同時也說明當(dāng)函數(shù)類型為int或char時,可缺省函數(shù)類型說明。
6、max(int a, int b) return ab?a:b; ,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),函數(shù)類型說明,(3) 函數(shù)有多個返回值 這時在被調(diào)函數(shù)的函數(shù)體中一般無return語句。多個值的返回是通過全局變量、數(shù)組或指針作參數(shù)來實現(xiàn)的,這將在以后的章節(jié)中進(jìn)行介紹。,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),形參表及形參說明,在函數(shù)定義中寫在圓括號中的參數(shù)稱為形式參數(shù)。不帶參數(shù)的函數(shù)其形參表是空的。一般無參函數(shù)在圓括號中可以寫上“void”以取代空的形參表。例如: void PRINT( void) printf(“This is a examplen”); 有參函數(shù)的形
7、參表由一個或多個形參組成,各參數(shù)間用逗號分隔。如果有形參,則必須定義形參的數(shù)據(jù)類型。形參類型有兩種定義格式:,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),形參表及形參說明, 傳統(tǒng)風(fēng)格 函數(shù)類型 函數(shù)名(形參表) 形參類型; 函數(shù)體; 例如: int max(a,b) int a,b; , 現(xiàn)代風(fēng)格 函數(shù)類型 函數(shù)名(形參表及類型) 函數(shù)體; 例如: int max(int a, int b) ,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),參數(shù)值的傳遞,函數(shù)間通過參數(shù)傳遞數(shù)據(jù),是通過調(diào)用函數(shù)中的實在參數(shù)(簡稱實參)向被調(diào)用函數(shù)中的形式參數(shù)(簡稱形參)傳遞進(jìn)行的。 實參向形參傳遞數(shù)據(jù)的方式:是
8、實參將值單向傳遞給形參,形參值的變化不影響實參值。,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),例如: void swap( int x,int y) int z; z=x;x=y;y=z; main( ) int a=5,b=10; swap(a,b); printf(“a=%d,b=dn”,a,b); ,程序執(zhí)行后,運(yùn)行結(jié)果為: a=5,b=10 而不是: a=10,b=5,參數(shù)值的傳遞,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),參數(shù)值的傳遞,定義函數(shù)時,定義的形參并不占用實際的存儲單元,只有在被調(diào)用時才由系統(tǒng)給它分配存儲單元,在調(diào)用結(jié)束后,形參所占用的存儲單元被釋放; 實參的個數(shù)與
9、類型應(yīng)與形參一致,否則將會出現(xiàn)編譯錯; C語言規(guī)定,函數(shù)間的參數(shù)傳遞是“值傳遞”,即值的單向傳遞,實參可以把值傳給形參,但形參的值不能傳給實參,也就是對形參的修改是不會影響到對應(yīng)的實參; 數(shù)組名作為參數(shù)傳遞的是數(shù)組首地址,嚴(yán)格說,其傳遞的也是“值(地址值)”,指針變量作參數(shù)同此。(后面詳講),4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),函數(shù)體說明,函數(shù)類型說明 函數(shù)名(形參說明表) 說明部分; 執(zhí)行部分; ,說明部分是對函數(shù)中要用到的變量、要調(diào)用到的函數(shù)以及要引用的外部變量進(jìn)行說明。,執(zhí)行部分是用來實現(xiàn)函數(shù)的功能,即語句執(zhí)行部分。,函數(shù)體,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),函數(shù)體
10、說明,被調(diào)用函數(shù)的函數(shù)體中無return語句,或return語句不帶表達(dá)式并不表示沒有返回值,而是表示返回一個不確定的值。如果不希望有返回值,必須在定義函數(shù)時把“函數(shù)類型說明符”說明為void。 函數(shù)有返回值時,函數(shù)體中至少有一條return語句,執(zhí)行到哪一個返回語句,該返回語句就起作用。但通過它只能得到一個數(shù)據(jù),多個數(shù)據(jù)的返回不能通過return語句實現(xiàn)。,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),函數(shù)體說明,printstar() printf(*); main() int a; a=printstar(); printf(“a=%d,a); ,輸出:a=10(不確定的值),int m
11、ax(int a,int b) if(ab) return a; else return b; main() int x=4,y=5,z; z=max(x,y); printf(“max=%d,z); ,多個return語句,但只1個被執(zhí)行,4.2函數(shù)的定義,4.2.2 函數(shù)定義的要點(diǎn),例4.2編程求2個正整數(shù)的最大公因子,int fun(int m, int n) int r; r=m%n; while( r ) m=n;n=r;r=m%n; return n; ,#include main( ) int x,y,z; scanf(%d,%d, ,例如輸入:32,48, 則輸出:z=16 (
12、教材上多一條if語句實現(xiàn)交換,結(jié)果一樣),4.2函數(shù)的定義,4.2.3 函數(shù)的聲明,函數(shù)已定義,如果要調(diào)用,一般應(yīng)在主調(diào)函數(shù)中對被調(diào)函數(shù)進(jìn)行聲明,即向編譯系統(tǒng)聲明將要調(diào)用此函數(shù),并將有關(guān)信息(如被調(diào)用函數(shù)名、函數(shù)類型、形參的個數(shù)及類型等)通知編譯系統(tǒng)。,函數(shù)聲明的含義,4.2函數(shù)的定義,4.2.3 函數(shù)的聲明,函數(shù)聲明的一般形式,(1)函數(shù)類型 函數(shù)名(形參類型1參數(shù)1,形參類型2參數(shù)2, );,(2)函數(shù)類型 函數(shù)名(形參類型1,形參類型2,);,如:int max(int a, int b);,如:int max(int, int);,因編譯系統(tǒng)不檢查參數(shù)名,故格式(1)(2)效果一樣,例
13、4.3求兩個數(shù)之和。 #include main( ) float fun(float x,float y); printf(“Sum=%fn”,fun(2,5); float fun(float x,float y) return x+y; ,4.2函數(shù)的定義,4.2.3 函數(shù)的聲明,函數(shù)聲明的一般形式,例4.3求兩個數(shù)之和。 #include main( ) float fun(float,float); printf(“Sum=%fn”,fun(2,5); float fun(float x,float y) return x+y; ,Sum=7.000000,Sum=7.000000,
14、例4.3求兩個數(shù)之和。 #include main( ) float fun( ); printf(“Sum=%fn”,fun(2,5); float fun(float x,float y) return x+y; ,4.2函數(shù)的定義,4.2.3 函數(shù)的聲明,函數(shù)聲明的一般形式,例4.3求兩個數(shù)之和。 #include main( ) printf(“Sum=%fn”,fun(2,5); float fun(float x,float y) return x+y; ,Sum=0.000000,Error: Type mismatch,例4.3求兩個數(shù)之和。 #include float fu
15、n(float x,float y) return x+y; main( ) printf(“Sum=%fn”,fun(2,5); ,4.2函數(shù)的定義,4.2.3 函數(shù)的聲明,函數(shù)聲明的一般形式,Sum=7.000000,對于用戶自定義函數(shù),若被調(diào)用函數(shù)定義在主調(diào)函數(shù)之前,可缺省聲明。,示例求兩個數(shù)之和。 #include main( ) printf(Sum=%dn,fun(2,5); int fun(int x,int y) return x+y; ,4.2函數(shù)的定義,4.2.3 函數(shù)的聲明,函數(shù)聲明的一般形式,Sum=7,對于用戶自定義函數(shù),若被調(diào)函數(shù)返回值是int或char時,則函數(shù)聲
16、明可缺省。,示例求兩個數(shù)之和。 #include main( ) printf(Sum=%dn,fun(2.7,5); int fun(int x,int y) return x+y; ,4.2函數(shù)的定義,4.2.3 函數(shù)的聲明,函數(shù)聲明的一般形式,Sum=13107,對于用戶自定義函數(shù),若被調(diào)函數(shù)返回值是int或char時,則函數(shù)聲明可缺省。但缺省后,系統(tǒng)無法對參數(shù)類型作檢查,若調(diào)用函數(shù)時參數(shù)使用不當(dāng),編譯不報錯,但執(zhí)行結(jié)果會出錯。,4.2函數(shù)的定義,4.2.3 函數(shù)的聲明,關(guān)于函數(shù)聲明的小結(jié),在今后的編程實踐中,養(yǎng)成如下習(xí)慣就不會出現(xiàn)意想不到的錯誤。 將自定義函數(shù)在主調(diào)函數(shù)之前進(jìn)行定義,則
17、可缺省函數(shù)聲明。 若自定義函數(shù)在主調(diào)函數(shù)之后進(jìn)行定義,則嚴(yán)格按照兩種聲明格式在調(diào)用前進(jìn)行函數(shù)聲明。,4.3遞歸函數(shù),4.3.1 遞歸的概念,一個函數(shù)在它的函數(shù)體內(nèi)直接或間接的調(diào)用它自身,則該函數(shù)稱之為遞歸函數(shù)。 直接遞歸:函數(shù)直接調(diào)用自已。(常用:本節(jié)重點(diǎn)討論) 間接遞歸:函數(shù)間接調(diào)用自己。(不常用,本節(jié)不討論),返回目錄,4.3遞歸函數(shù),4.3.1 遞歸的概念,例4.5用遞歸法求f(n)=n! 計算階乘可用如下形式描述:,#include unsigned long fact(int n) unsigned long p; if(n= =1) p=1; else p=n*fact(n-1);
18、 return p; main( ) int n; unsigned long p; scanf(“%d”,語句,調(diào)用過程會怎樣?,4.3遞歸函數(shù),4.3.1 遞歸的概念,一般形式1: 返值類型遞歸函數(shù)名fun(參數(shù)說明表) if(遞歸終止條件) 返回值p = 遞歸終止值;/*遞歸終止*/ else 返回值p = 遞歸調(diào)用fun()的表達(dá)式;/*遞歸調(diào)用*/ return p; ,定義遞歸函數(shù)的一般形式,4.3遞歸函數(shù),4.3.1 遞歸的概念,一般形式2: 返值類型遞歸函數(shù)名fun(參數(shù)說明表) if(遞歸終止條件) return 遞歸終止值;/*遞歸終止*/ else return 遞歸調(diào)用
19、fun()的表達(dá)式;/*遞歸調(diào)用*/ ,定義遞歸函數(shù)的一般形式,4.3遞歸函數(shù),4.3.2 遞歸舉例,附例:階乘函數(shù) 非遞歸形式遞歸形式,int fact( int n ) if( n = 0 ) /*遞歸終止條件*/ return 1; /*遞歸終止值*/ else return( n * fact(n-1); /*遞歸調(diào)用*/ ,附例:斐波那契(Fibonacci)數(shù)列 非遞歸形式遞歸形式,int fib( int n ) if(n=0 | n=1) /*遞歸終止條件*/ return n; /*遞歸終止值*/ else return(fib(n-1)+fib(n-2); /*遞歸調(diào)用*/
20、 ,4.3遞歸函數(shù),4.3.2 遞歸舉例,4.3遞歸函數(shù),圓盤移動規(guī)則 1)每次只能移動一個圓盤; 2)圓盤可以插在X,Y和Z中的任一塔座上; 3)任何時刻都不能將一個較大的圓盤壓在較小的圓盤上。,例4.7 n階Hanoi塔問題。,3階Hanoi塔問題的初始與結(jié)果狀態(tài),4.3.2 遞歸舉例,4.3遞歸函數(shù),(1)從A移動1n-1號圓盤至B,C作輔助;(遞歸調(diào)用),分析此問題可歸之于三個子問題,(2)從A移動n號圓盤至C; (一次搬運(yùn)),(3)從B移動1n-1號圓盤至C,A作輔助。(遞歸調(diào)用),A,B,C,1n-1,n,4.3.2 遞歸舉例,4.3遞歸函數(shù),void move(int n,cha
21、r a,char b,char c) if( n0 ) move(n-1,a,c,b); printf(“%c-%cn”,a,c); move(n-1,b,a,c); ,解答求解n階Hanoi塔問題的遞歸算法。,(1)從A移動1n-1號圓盤至B,C作輔助;(遞歸調(diào)用),(2)從A移動n號圓盤至C; (一次搬運(yùn)),(3)從B移動1n-1號圓盤至C,A作輔助。(遞歸調(diào)用),4.3.2 遞歸舉例,4.4存儲類型、生存期和作用域,4.4.1 存儲類型,定義一個變量的一般形式為: 存儲類型 數(shù)據(jù)類型 變量標(biāo)識符表;,變量的數(shù)據(jù)類型規(guī)定了變量的存儲空間大小和取值范圍;(已介紹) 變量的存儲類型規(guī)定了變量的
22、生存期和作用域。變量的存儲類型有4個,分別是自動型、寄存器型、外部型和靜態(tài)型,其說明符分別是auto、register、extern和static。,返回目錄,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,生存期,就是變量存活的周期。為了節(jié)省內(nèi)存,避免變量互相干擾,不可能讓所有的變量始終存在。有兩種情況: 一是變量從程序開始定義就被分配內(nèi)存(即產(chǎn)生),直到程序運(yùn)行結(jié)束內(nèi)存釋放,這種變量稱為靜態(tài)變量; 二是變量帶有臨時性,隨調(diào)用的模塊(如文件、函數(shù)或復(fù)合語句)分配內(nèi)存單元,模塊調(diào)用結(jié)束,釋放內(nèi)存,這種變量稱為動態(tài)變量。,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,所
23、謂變量的作用域,就是某個變量在其生命期內(nèi),程序模塊是否可使用該變量。有兩種情況: 一種變量在從定義點(diǎn)開始或整個源程序都可有效,稱為全局變量; 另一種變量只能在所定義的模塊內(nèi)部有效,稱為局部變量。,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,局部變量,局部變量定義在函數(shù)內(nèi)部。 不同函數(shù)中可以使用相同名字的變量,它們代表不同的對象,互不干擾。 函數(shù)的形參也是局部變量,只在所在函數(shù)中有效。 在函數(shù)內(nèi)部復(fù)合語句中定義的變量,只在該復(fù)合語句中有效,出了復(fù)合語句就無效。,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,全局變量,全局變量定義在函數(shù)之外。 全局變量的有效范圍是從定
24、義的位置開始到整個程序的結(jié)束。 局部變量可以和全局變量同名,若同名,則在局部變量的作用域內(nèi),全局變量被“屏蔽”(即不起作用)。 全局變量的作用降低了函數(shù)的通用性,建議盡量少用或不用。,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,auto存儲類型變量,在各個函數(shù)內(nèi)部或復(fù)合語句內(nèi)定義的變量。 自動變量用“auto”進(jìn)行標(biāo)識,可缺省。例如,在函數(shù)體中,定義auto int b,c=3;與定義int b,c=3;是等價的。 自動變量的生存期:自定義開始,直到所在函數(shù)或復(fù)合語句結(jié)束。 自動變量的作用域:就是在定義該變量的函數(shù)或復(fù)合語句內(nèi)。,4.4存儲類型、生存期和作用域,4.4.2 生存
25、期和作用域,auto存儲類型變量,#include add(int x) x+; printf(“%d,”,x); sub( int x) x- -; printf(“%d,”,x); main( ) int y=1; add(y); sub(y); add(y); sub(y); ,程序運(yùn)行的結(jié)果為: 2,0,2,0,例4.8auto型變量特性的分析。,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,auto存儲類型變量,#include main( ) int x=1,y=2; int x= 10; x=x+10; printf(“x=%d,”,x); printf(“y=%d
26、,”,y); x=x+9; printf(“x=%dn”,x); ,例4.9auto型變量作用域示例。,程序運(yùn)行結(jié)果為:x=20,y=2,x=10,外層模塊變量x的作用域被內(nèi)層模塊同名變量的作用域屏蔽,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,register存儲類型變量,如果有一些變量使用頻繁(例如在一個函數(shù)中執(zhí)行1000000次循環(huán),每次循環(huán)中都要引用某局部變量),則要為存取該變量的值花不少時間。 為提高執(zhí)行效率,C語言允許將局部變量的值放在CPU中的寄存器中,需要用時直接從寄存器中取出參加運(yùn)算。由于對寄存器的存取速度遠(yuǎn)遠(yuǎn)高于對內(nèi)存的讀取速度,因此這樣可以提高執(zhí)行效率。這
27、種變量稱為“寄存器變量”,用關(guān)鍵字register聲明。,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,register存儲類型變量,例如下面的程序段: long i, sum=0; for(i=1;i=1000000;i+) sum=sum+i; 可改成: register long i, sum=0; for(i=1;i=1000000;i+) sum=sum+i; 說明:當(dāng)今的優(yōu)化編譯系統(tǒng)能夠識別使用頻繁的變量,從而自動地將這些變量放在寄存器中,而不需要程序設(shè)計者指定。因此在實際上用register聲明變量是不必要的。讀者對它有一定了解即可。,4.4存儲類型、生存期和作用域
28、,4.4.2 生存期和作用域,外部變量,定義在所有函數(shù)(包括主函數(shù))外部的變量稱為外部變量。 對于外部變量,系統(tǒng)在編譯時是將外部變量的內(nèi)存單元分配在靜態(tài)數(shù)據(jù)存儲區(qū),在整個程序文件運(yùn)行結(jié)束后系統(tǒng)才收回其存儲單元。因此外部變量的生存期:是從外部變量定義點(diǎn)開始,直至源程序運(yùn)行結(jié)束。 作用域:是從定義點(diǎn)開始,直到程序結(jié)束。在定義點(diǎn)之前或別的源程序中要引用外部變量,則在引用前,需進(jìn)行聲明(作用域擴(kuò)展)。,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,外部變量,例4.10用函數(shù)實現(xiàn),求ax2+bx+c=0(a0)根。 解題思路在a0的情況下,根據(jù)b*b-4*a*c的不同值,方程分別有兩個不等
29、的實根、兩個相等的實根和兩個共軛的虛根。如果傳遞方程的3個系數(shù)a、b、c給函數(shù),分別求出方程的根。上述三種情況都需要返回兩個數(shù)據(jù),通過return語句無法實現(xiàn),因此可借用外部變量實現(xiàn)。 程序如下:,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,外部變量,#include #include float x1,x2; /*定義外部變量*/ void f1(float a,float b,float c) x1=(-b+sqrt(b*b-4*a*c)/(2*a); x2=(-b-sqrt(b*b-4*a*c)/(2*a); void f2(float a,float b) x1=x2=
30、-b/(2*a); void f3(float a,float b,float c) x1=-b/(2*a); x2=sqrt(4*a*c-b*b)/(2*a); ,main( ) float a,b,c; scanf(“%f%f%f”, ,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,外部變量,例4.11外部變量引用示例 #include int x=10; /*外部變量x的定義*/ void f1( ) x+; printf(“x=%d,”,x); printf(“y=%fn”,y); float y=2;/*外部變量y的定義*/ main( ) int x=1; x+; f
31、1( ); printf(“x=%d,y=%fn”,x,y); ,編譯結(jié)果: Error: y : undeclared identifier,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,外部變量,例4.11外部變量引用示例 #include int x=10; /*外部變量x的定義*/ void f1( ) extern float y; /*外部變量聲明,擴(kuò)展了外部變量y的作用域*/ x+; printf(“x=%d,”,x); printf(“y=%fn”,y); float y=2;/*外部變量y的定義*/ main( ) int x=1; x+; f1( ); pri
32、ntf(“x=%d,y=%fn”,x,y); ,運(yùn)行結(jié)果: x=11,y=2.000000 x=2,y=2.000000,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,靜態(tài)變量,靜態(tài)變量分靜態(tài)局部變量和靜態(tài)全局變量。 (1) 靜態(tài)局部變量 靜態(tài)局部變量是定義在函數(shù)體的復(fù)合語句中,用關(guān)鍵字“static”進(jìn)行標(biāo)識的變量。靜態(tài)局部變量定義的一般形式為: static 變量數(shù)據(jù)類型 變量名表;,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,靜態(tài)變量,對于靜態(tài)局部變量,系統(tǒng)在編譯時將其內(nèi)存單元分配在靜態(tài)數(shù)據(jù)存儲區(qū),并在編譯時賦初值(即只賦初值一次),在程序運(yùn)行時它已有初值,
33、以后再次使用時不必重新分配內(nèi)存單元和賦初值,其值是保留上一次函數(shù)調(diào)用結(jié)束時的值。直到程序運(yùn)行結(jié)束,對應(yīng)的內(nèi)存單元才釋放。 生存期:從變量定義開始,直到程序運(yùn)行結(jié)束。 作用域:在所定義的函數(shù)或復(fù)合語句中有效。 因此靜態(tài)局部變量是一種具有全局壽命、局部可見的變量。,例4.12靜態(tài)局部變量示例。 #include void f1( ) int x=1; x+; printf(x=%d,x); void f2( ) static int x=1; x+; printf(x=%dn,x); ,main( ) f1( ); f2( ); f1( );f2( ); 程序運(yùn)行結(jié)果為: x=2,x=2 x=2,
34、x=3,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,靜態(tài)變量,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,靜態(tài)變量,例4.13利用靜態(tài)局部變量求 10!,程序如下: #include long int fun(int n) static long int s=1; s=s*n; return s; main( ) int n; long int p; for(n=1;n=10;n+) p=fun(n); printf(“%ldn”,p); ,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,靜態(tài)變量,(2) 靜態(tài)全局變量 定義在所有函數(shù)(包括主函數(shù))之
35、外,用關(guān)鍵字“static”標(biāo)識的變量,稱為靜態(tài)全局變量。 生存期:靜態(tài)全局變量和外部變量都具有全局壽命,即在整個程序運(yùn)行期間都存在。 作用域:靜態(tài)全局變量只能在所定義的文件中使用,具有局部可見性。這與外部變量不同。 注意:自動變量沒有賦初值時,其值是一個隨機(jī)值。對于靜態(tài)變量或外部變量沒有賦初值時,數(shù)值型變量的值系統(tǒng)默認(rèn)為0。,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,靜態(tài)變量,例4.14靜態(tài)全局變量與外部變量的區(qū)別。 第一個文件的內(nèi)容如下: /*file1.c*/ #include int a=2; static int b=3; func( ) a+; b+; print
36、f(a=%d,b=%dn,a,b); ,第二個文件的內(nèi)容如下: /*file2.c*/ #include extern int a; int b; main( ) func( ); printf(a=%d, b=%dn,a,b); ,4.4存儲類型、生存期和作用域,4.4.2 生存期和作用域,靜態(tài)變量,將兩文件置于同一工程中。 程序運(yùn)行結(jié)果如下: a=3,b=4 a=3,b=0 從上述運(yùn)行結(jié)果可以看出,文件file1.c中的外部變量a可以在文件file2.c中使用,只需在file2.c中加上聲明語句:extern int a;即可.,Linker Error: error LNK2001: u
37、nresolved external symbol _b,但file1.c中的靜態(tài)全局變量b不能在file2.c中使用。讀者可將file2.c中變量定義語句:int b;改寫成變量聲明語句: extern int b; 想一想,會出現(xiàn)什么錯誤?為什么?,4.4存儲類型、生存期和作用域,4.4.3 存儲類型小結(jié),按作用域分類:局部變量和全局變量,4.4存儲類型、生存期和作用域,4.4.3 存儲類型小結(jié),4.4存儲類型、生存期和作用域,4.4.3 存儲類型小結(jié),目前我們在一般的編程實踐中,絕大部分涉及到的都是“自動變量”(auto),偶爾會涉及到“靜態(tài)局部變量”(static)或“外部變量”(ex
38、tern)。而“自動變量”(auto)在書寫時可以缺省,所以,變量定義時一般不必考慮存儲類型,但要重點(diǎn)理解“自動變量”的作用域與生存期。,4.5編譯預(yù)處理,4.5.0 引言,編譯預(yù)處理是在對源程序進(jìn)行正式編譯之前的處理。 這些預(yù)處理命令是ANSI C統(tǒng)一規(guī)定的,不能直接對它們進(jìn)行編譯(包括詞法和語法分析、代碼生成、優(yōu)化等),而必須在編譯之前,先對程序中這些特殊命令進(jìn)行“預(yù)處理”,然后再由編譯程序?qū)︻A(yù)處理后的源程序進(jìn)行正常的編譯處理。,返回目錄,4.5編譯預(yù)處理,4.5.0 引言,編譯預(yù)處理是在對源程序進(jìn)行正式編譯之前的處理。 例1:若程序中用#define命令定義了一個符號常量A(#defin
39、e A 100),則在預(yù)處理時將程序中所有A都置換為指定的值(100)。 例2:若程序中用#include命令包含了一個文件“stdio.h”(#include,則在預(yù)處理時將stdio.h文件中的實際內(nèi)容代替該命令,4.5編譯預(yù)處理,4.5.0 引言,C提供的預(yù)處理功能主要有以下三種: 1)文件包含 2)宏定義 3)條件編譯 為了與一般C語句相區(qū)別,這些命令前以符號“”開頭。并占用一個單獨(dú)的書寫行,語句結(jié)尾不加分號。,4.5編譯預(yù)處理,4.5.1 文件包含,文件包含命令的作用,如果文件A中有一條文件包含預(yù)處理命令: #include 該命令將指定文件B的內(nèi)容加到文件A中“#include ”
40、命令處的位置,共同組成一個程序文件,即在文件A中產(chǎn)生文件B的一個副本。 例如: #include /*把含有輸入/輸出標(biāo)準(zhǔn)函數(shù)的頭文件包含到當(dāng)前源程序文件中*/ #include /*把含有標(biāo)準(zhǔn)數(shù)學(xué)函數(shù)的頭文件包含到當(dāng)前源程序文件中*/,4.5編譯預(yù)處理,4.5.1 文件包含,文件包含命令的作用,圖4.6是一個文件包含關(guān)系的示意圖。文件file1.c中的包含命令#include file2.c將文件file2.c包含進(jìn)文件file1.c。圖4.6(a)和圖4.6(b)是預(yù)處理前的情況,圖4.6(c)是將文件file2.c包含進(jìn)文件file1.c之后的file1.c結(jié)構(gòu)示意圖。,圖4.6文件包含
41、示意圖,4.5編譯預(yù)處理,4.5.1 文件包含,#include命令的兩種形式,格式一:#include 本命令的特點(diǎn)是在文件名的首尾用尖括號括起來。文件名可以帶路徑。在預(yù)處理時將只在所指定的標(biāo)準(zhǔn)目錄(即C系統(tǒng)安裝后形成的include子目錄,該子目錄中有系統(tǒng)提供的頭文件)中查找包含文件。,4.5編譯預(yù)處理,4.5.1 文件包含,#include命令的兩種形式,格式二:#include 文件名 本命令格式的特點(diǎn)是在文件名的首尾用雙引號括起來。其中,文件名指出的是待包含進(jìn)來的文件,且可以帶路徑。若文件名帶路徑,則預(yù)處理時在指定的路徑下去查找。若查找不到,再到系統(tǒng)指定的標(biāo)準(zhǔn)目錄中找。找到文件后,用
42、文件內(nèi)容替換該命令。因此格式二的查找功能包含了格式一的查找功能。另外,兩種格式的include后可不帶空格。,4.5編譯預(yù)處理,4.5.1 文件包含,例4.14設(shè)計一個求n!的函數(shù),存放于文件exam.c中,然后設(shè)計主函數(shù)文件file.c,計算p=n!/m!/(n-m)! 可以編寫文件exam.c如下: /*exam.c*/ long int fact(int n) long int p=1; int k; for(k=1;k=n;k+) p=p*k; return p; ,另一文件file.c的內(nèi)容如下: /*file.c*/ #include #include exam.c main( )
43、 int m,n; long int p; printf“Input n,m(nm):”); scanf(“%d,%d”,printf(“world!n”); 在預(yù)處理時,系統(tǒng)在該宏定義以后出現(xiàn)的每一個宏名都用宏體來代替,這個過程叫宏替換。,4.5編譯預(yù)處理,4.5.2 宏定義,1. 不帶參的宏,例4.16求球的表面積和體積。 #include #define PI 3.1415926 main( ) float s,v,r; scanf(“%f”, 在預(yù)處理時,系統(tǒng)會將main函數(shù)中的PI自動用3.1415926替換。上述文件將變?yōu)椋?#include main( ) float s,v,r
44、; scanf(“%f”,printf(world!n); main( ) MSG ,Hello,world!,程序運(yùn)行結(jié)果:,4.5編譯預(yù)處理,4.5.2 宏定義,2. 帶參的宏,帶參數(shù)宏定義的一般形式為: #define 宏名(參數(shù)) 宏體 例如: #define SQR(x) (x)*(x) #define MOD(x,y) (x)%(y) 預(yù)處理時,系統(tǒng)在用宏體代替宏名的同時,實在參數(shù)會代替宏體中形式參數(shù),同樣宏替換仍只是一種簡單的替換,不能進(jìn)行計算或其它的功能。 當(dāng)程序出現(xiàn)下列語句時: y=SQR(a+b); 程序在預(yù)處理時,將被替換成如下語句: y=(a+b)*(a+b);,4.5
45、編譯預(yù)處理,4.5.2 宏定義,2. 帶參的宏,注意:在定義帶參數(shù)的宏時,宏體中的所有形式參數(shù)和整個表達(dá)式最好都加圓括號,否則在宏替換后的表達(dá)式中,運(yùn)算順序可能會發(fā)生不希望的變化。,例如4.17帶參宏定義示例。 #include #define SQR(x) (x)*(x) main( ) int y; y=SQR(2+1); printf(“%dn”,y); 上述程序運(yùn)行結(jié)果為:9,若將宏定義改成如下形式: #include #define SQR(x) x*x main( ) int y; y=SQR(2+1); printf(“%dn”,y); 上述程序運(yùn)行結(jié)果為:5,思考:為什么?,4
46、.5編譯預(yù)處理,4.5.2 宏定義,2. 帶參的宏,帶參數(shù)的宏定義可以嵌套調(diào)用其它已經(jīng)存在的宏,即下一個宏定義的宏體可以出現(xiàn)上面已經(jīng)定義過的宏名。例如: #define SQR(x) (x)*(x) #define CUBE(x) SQR(x)*(x) 語句CUBE(a+b)進(jìn)行宏展開后,變?yōu)椋?(a+b)*(a+b)*(a+b),4.5編譯預(yù)處理,4.5.3 條件編譯,一般情況下,源程序的所有語句都會參加編譯。但有時若希望只對其中的部分滿足條件的語句進(jìn)行編譯,則就要用到“條件編譯”。 條件編譯是在對源程序編譯之前的處理中,根據(jù)給定的條件,決定只編譯其中的某一部分源程序,而不編譯另外一部分源程
47、序。 條件編譯有如下3種形式:,4.5編譯預(yù)處理,4.5.3 條件編譯,1. 格式一,一般形式為: #ifdef 標(biāo)識符 程序段1 #else 程序段2 #endif 功能是:如果標(biāo)識符已經(jīng)被#define命令定義過,則在程序編譯時只對程序段1進(jìn)行編譯,否則只對程序段2進(jìn)行編譯。其中的程序段即可以是一條語句,也可以是一組語句。如果是一組語句,也不必象復(fù)合語句一樣加上花括號。,4.5編譯預(yù)處理,4.5.3 條件編譯,1. 格式一,例4.18條件編譯命令格式一示例。 #include #define OK 1 #ifdef OK #define STRING you have defined OK
48、! #define STRING1 nOK=1 #else #define STRING you have not defined OK! #define STRING1 nOK未定義 #endif main( ) printf(STRING); printf(STRING1); ,4.5編譯預(yù)處理,4.5.3 條件編譯,1. 格式一,例4.18條件編譯命令格式一示例。 #include #ifdef OK #define STRING you have defined OK! #define STRING1 nOK=1 #else #define STRING you have not de
49、fined OK! #define STRING1 nOK未定義 #endif main( ) printf(STRING); printf(STRING1); ,4.5編譯預(yù)處理,4.5.3 條件編譯,1. 格式一,注意:標(biāo)識符在預(yù)處理#define命令中可以被定義為任何字符,甚至后面什么也不寫,如: #define OK 1 #define OK a #define OK 如果沒有程序段2,本格式可以簡化成如下形式: #ifdef 標(biāo)識符 程序段1 #endif,4.5編譯預(yù)處理,4.5.3 條件編譯,2. 格式二,一般形式為: #ifndef 標(biāo)識符 程序段1 #else 程序段2 #e
50、ndif 格式二與格式一的不同之處是將“ifdef”改成“ifndef”。其功能是:如果標(biāo)識符沒有被#define命令定義過,則在程序編譯時只對程序段1進(jìn)行編譯,否則只對程序段2進(jìn)行編譯。這與格式一的功能恰好相反。但二者在用法上是相似的,不再贅述!,4.5編譯預(yù)處理,4.5.3 條件編譯,3. 格式三,一般形式為: #if 常量表達(dá)式 程序段1 #else 程序段2 #endif 功能是:如果常量表達(dá)式的值為真(即非0),則在程序編譯時只對程序段1進(jìn)行編譯,否則只對程序段2進(jìn)行編譯。請看例4.19。,4.5編譯預(yù)處理,4.5.3 條件編譯,3. 格式三,與條件語句的區(qū)別:若用條件語句將會對整個
51、源程序進(jìn)行編譯,造成目標(biāo)程序長,運(yùn)行時間長;而采用條件編譯,可減少被編譯的語句,從而減少了目標(biāo)程序的長度和運(yùn)行時間。,例4.19條件編譯命令格式三示例。 #include #define A 2 main( ) #if A0 printf(A0); #else printf(A0 or A=0); #endif ,A0,程序運(yùn)行結(jié)果:,4.5編譯預(yù)處理,4.5.3 條件編譯,條件編譯的應(yīng)用舉例,在調(diào)試程序時,常常希望輸出一些所需的信息,用以檢查程序執(zhí)行過程是否有誤,而在調(diào)試完成并交付用戶使用的軟件版本中又不再需要輸出這些信息。則可以在程序中采取條件編譯。,#include main( ) in
52、t n=1,s=0; while(n=10) s=s+n; n+; printf(s=%dn,s); ,左例是求1到10之和的程序,若在調(diào)試時希望知道每一步n和s值的變化,可用條件編譯,程序見下頁:,4.5編譯預(yù)處理,4.5.3 條件編譯,條件編譯的應(yīng)用舉例,#include #define _DEBUG main( ) int n=0,s=0; while(n10) n+; s=s+n; #ifdef _DEBUG printf(Cycle %d:n=%d,s=%dn,n,n,s); #endif printf(s=%dn,s); ,程序運(yùn)行結(jié)果,思考:刪去第2行,結(jié)果?,4.5編譯預(yù)處理,
53、4.5.3 條件編譯,條件編譯的應(yīng)用舉例,在上例中,若不需要顯示循環(huán)執(zhí)行過程中的值時,則只要將#define _DEBUG命令行刪去即可。刪除后,程序中所有用_DEBUG作標(biāo)識符的條件編譯段中相應(yīng)語句都不起作用,即起到統(tǒng)一控制的作用,如同一個“總開關(guān)”一樣。 在Microsoft Visual C+的系列軟件中,均有debug(調(diào)試版)和release(發(fā)行版)兩種配置設(shè)置。其中debug版配置中一般定義了_DEBUG標(biāo)識符。實際上就是在利用條件編譯的思想。,4.6習(xí)題四, 單項選擇題,C語言中函數(shù)形參的缺省存儲類型是 . A)靜態(tài)(static)B)自動(auto) C)寄存器(regist
54、er) D)外部(extern) 函數(shù)調(diào)用語句function(exp1,exp2), 18)中含有的實參個數(shù)為. A)0 B)1 C)2D)3,B,C,返回目錄,4.6習(xí)題四, 單項選擇題,下面函數(shù)返回值的類型是 . square(float x) return x*x; A)與參數(shù)x的類型相同B)是void型 C)無法確定 D)是int型 C語言規(guī)定,程序中各函數(shù)之間 . A)不允許直接遞歸調(diào)用,也不允許間接遞歸調(diào)用 B)允許直接遞歸調(diào)用,但不允許間接遞歸調(diào)用 C)不允許直接遞歸調(diào)用,但允許間接遞歸調(diào)用 D)既允許直接遞歸調(diào)用,也允許間接遞歸調(diào)用,D,D,4.6習(xí)題四, 單項選擇題,一個函
55、數(shù)返回值的類型取決于 . A)return語句中表達(dá)式的類型 B)調(diào)用函數(shù)時臨時指定 C)定義函數(shù)時指定或缺省的函數(shù)類型 D)調(diào)用該函數(shù)的主調(diào)函數(shù)的類型 下面敘述中,錯誤的是 . A)函數(shù)的定義不能嵌套,但函數(shù)調(diào)用可以嵌套 B)為了提高可讀性,編寫程序時應(yīng)該適當(dāng)使用注釋 C)變量定義時若省去了存儲類型,系統(tǒng)將默認(rèn)其為靜態(tài)變量 D)函數(shù)中定義的局部變量的作用域在函數(shù)內(nèi)部,C,C,4.6習(xí)題四, 單項選擇題,7) 在一個源程序文件中定義的全局變量的有效范圍為 . A)一個C程序的所有源程序文件 B)該源程序文件的全部范圍 C)從定義處開始到該源程序文件結(jié)束 D)函數(shù)內(nèi)全部范圍 某函數(shù)在定義時未指明
56、函數(shù)返回值類型,且函數(shù)中沒有return語句,現(xiàn)若調(diào)用該函數(shù),則正確的說法是 . A)沒有返回值B)返回一個用戶所希望的值 C)返回一個系統(tǒng)默認(rèn)值D)返回一個不確定的值,C,D,4.6習(xí)題四, 單項選擇題,9)函數(shù)swap(int x, int y)可實現(xiàn)對x和y值的交換。在執(zhí)行如下定義及調(diào)用語句后,a和b的值分別為 . int a=10, b=20; swap (a,b ); A)10和10B)10和20C)20和10D)20和20 下面錯誤的敘述是 . A)在某源程序不同函數(shù)中可以使用相同名字的變量 B)函數(shù)中的形式參數(shù)是局部變量 C)在函數(shù)內(nèi)定義的變量只在本函數(shù)范圍內(nèi)有效 D)在函數(shù)內(nèi)的復(fù)合語句中定義的變量在本函數(shù)范圍內(nèi)有效,B,D,4.6習(xí)題四, 程序填空題,1求s=1!+2!+3!+10!之和。 程序如下: #include long int factorial(int n) int k=1; long int p=1; for(k=1; k=n; k+) _; return p; ,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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 綜合素養(yǎng)提升的跨領(lǐng)域?qū)W習(xí)策略研究
- 科技驅(qū)動的校園環(huán)境改善策略
- IT行業(yè)保密協(xié)議(2024版)
- 2025年度智能廚電一體化購銷合同二零二五3篇
- 二零二五年度自助餐廳經(jīng)營承包合同3篇
- 漯河2024年河南漯河市沙澧河建設(shè)運(yùn)行保障中心人才引進(jìn)5人筆試歷年參考題庫附帶答案詳解
- 滁州安徽滁州明光市司法局招聘司法協(xié)理員7人筆試歷年參考題庫附帶答案詳解
- 高效能實驗的關(guān)鍵儀器的科學(xué)使用方法
- 淮安2025年江蘇淮安漣水縣公安局警務(wù)輔助人員招聘87人(一)筆試歷年參考題庫附帶答案詳解
- 二零二五年度蟲草產(chǎn)品研發(fā)與創(chuàng)新合同3篇
- 2024年小升初語文入學(xué)分班測試卷四(統(tǒng)編版)
- 流行文化對青少年價值觀的影響研究
- 2024年代理記賬工作總結(jié)6篇
- 電氣工程預(yù)算實例:清單與計價樣本
- VOC廢氣治理工程中電化學(xué)氧化技術(shù)的研究與應(yīng)用
- 煤礦機(jī)電設(shè)備培訓(xùn)課件
- 科技論文圖表等規(guī)范表達(dá)
- 高考寫作指導(dǎo)議論文標(biāo)準(zhǔn)語段寫作課件32張
- 2021年普通高等學(xué)校招生全國英語統(tǒng)一考試模擬演練八省聯(lián)考解析
- 紅色研學(xué)旅行課程的設(shè)計與實踐
- 幼兒園保育教育質(zhì)量指南評估指標(biāo)考核試題及答案
評論
0/150
提交評論