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

下載本文檔

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

文檔簡介

第七章函數(shù)及變量存儲(chǔ)類型7.1函數(shù)基礎(chǔ)與C程序結(jié)構(gòu)

7.2函數(shù)的定義和聲明7.3函數(shù)的調(diào)用7.4函數(shù)的嵌套與遞歸7.5變量的存儲(chǔ)類別7.6編譯預(yù)處理7.7程序設(shè)計(jì)舉例習(xí)題7.1函數(shù)基礎(chǔ)與C程序結(jié)構(gòu)7.1.1C程序的結(jié)構(gòu)化設(shè)計(jì)思想當(dāng)設(shè)計(jì)一個(gè)解決復(fù)雜問題的程序時(shí),傳統(tǒng)的面向過程的程序設(shè)計(jì)方法為了能清楚地描述程序的運(yùn)行過程,要求將一個(gè)復(fù)雜的任務(wù)劃分為若干子任務(wù),每個(gè)子任務(wù)設(shè)計(jì)成一個(gè)子程序,稱為模塊。若子任務(wù)較復(fù)雜,還可以將子任務(wù)繼續(xù)分解,直到分解成為一些容易解決的子任務(wù)為止。每個(gè)子任務(wù)對(duì)應(yīng)于一個(gè)子程序,子程序在程序編制(即代碼)上相互獨(dú)立,而在對(duì)數(shù)據(jù)的處理上又相互聯(lián)系,數(shù)據(jù)和程序代碼是分開存儲(chǔ)的。完成總?cè)蝿?wù)的程序由一個(gè)主程序和若干子程序組成,主程序起著任務(wù)調(diào)度的總控作用,而每個(gè)子程序各自完成一個(gè)單一的任務(wù)。這種自上而下逐步細(xì)化的模塊化程序設(shè)計(jì)方法就是所謂結(jié)構(gòu)化程序設(shè)計(jì)方法。結(jié)構(gòu)化程序設(shè)計(jì)的優(yōu)點(diǎn)是:程序編制方便,易于修改和調(diào)試,可由多人分工合作完成;程序結(jié)構(gòu)模塊化,可讀性、可維護(hù)性及可擴(kuò)充性強(qiáng);子程序代碼公用(當(dāng)需要完成同樣任務(wù)時(shí),只需要一段代碼,可多次調(diào)用),使程序簡潔。

C語言是函數(shù)式語言,沒有子程序,程序員可利用函數(shù)來實(shí)施結(jié)構(gòu)化程序設(shè)計(jì),即單一的程序任務(wù)由獨(dú)立的函數(shù)來完成,不要試圖在一個(gè)函數(shù)中完成所有的任務(wù),一個(gè)函數(shù)只應(yīng)完成一件單一的任務(wù)。一個(gè)成功的C程序應(yīng)該是由多個(gè)功能簡單、獨(dú)立的函數(shù)構(gòu)成的。要善于利用函數(shù),以減少重復(fù)編程。組成一個(gè)C程序的各函數(shù)可以分開編輯成多個(gè)C源文件。一個(gè)C源文件中可以含有0個(gè)(源文件中可以沒有函數(shù),僅由一些說明組成,例如定義一些全局變量)或多個(gè)函數(shù),因而一個(gè)C程序可以有一個(gè)或多個(gè)源文件,每個(gè)源文件是一個(gè)編譯單位。源文件被編譯之后生成二進(jìn)制代碼形式的目標(biāo)程序文件,組成一個(gè)C程序的所有源文件都被編譯之后,由連接程序?qū)⒏髂繕?biāo)文件中的目標(biāo)函數(shù)和系統(tǒng)標(biāo)準(zhǔn)函數(shù)庫的函數(shù)裝配成一個(gè)可執(zhí)行的C程序。

C程序結(jié)構(gòu)示意圖如圖7.1所示。圖7.1C程序結(jié)構(gòu)示意圖7.1.2函數(shù)概述在C程序設(shè)計(jì)中,函數(shù)是獨(dú)立的C程序模塊,它完成一個(gè)特定任務(wù)并可選擇是否將一個(gè)值返回調(diào)用程序。在C語言中,子程序被稱為函數(shù),它相應(yīng)于其它高級(jí)語言中的過程(無返回值的子程序)和函數(shù)(通過函數(shù)名返回一個(gè)值的子程序)。一個(gè)C程序一般由多個(gè)函數(shù)組成,其中必須有一個(gè)且僅有一個(gè)名為main的主函數(shù),其余為被main()函數(shù)或其它函數(shù)調(diào)用的函數(shù),無論main()函數(shù)位于程序中的什么位置,C程序總是從main()函數(shù)開始執(zhí)行。

main()函數(shù)可調(diào)用其它函數(shù)來實(shí)現(xiàn)所需的功能。被main()函數(shù)調(diào)用的函數(shù)分為兩類。一類是由系統(tǒng)提供的標(biāo)準(zhǔn)庫函數(shù),例如,標(biāo)準(zhǔn)輸入/輸出函數(shù)(scanf,printf,getche,putchar,…)、數(shù)學(xué)計(jì)算函數(shù)(sin,cos,fabs,sqrt,…)、數(shù)據(jù)格式轉(zhuǎn)換函數(shù)(atoi,atof,sscanf,sprintf,…)、字符串處理函數(shù)(strlen,strcpy,strcmp,…)和文件讀/寫函數(shù)(fread,fwrite,fopen,…)等。這類函數(shù)可以由用戶程序直接調(diào)用。另一類是用戶在自己的程序中定義的函數(shù),即需要由用戶自己編寫的函數(shù)。

例7.1

用戶自定義函數(shù)——求數(shù)的平方。

#include<stdio.h>

longsquare(long);/*函數(shù)聲明*/

voidmain()

{

longin_num,result;

printf("Inputaninteger:");

scanf("%ld",&in_num);

result=square(in_num);/*函數(shù)調(diào)用*/

printf("\nThesquarenumberof%ldis%ld",in_num,result);

}

longsquare(longx)/*函數(shù)定義*/

{

longx_square;/*說明部分*/

x_square=x*x;/*執(zhí)行部分*/

returnx_square;

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

Inputaninteger:100↙

(輸入)

Thesquarenumberof100is10000(輸出)分析:

(1)“l(fā)ongsquare(long);”語句是一個(gè)函數(shù)聲明,函數(shù)定義將在程序的后面出現(xiàn)。函數(shù)聲明指出了函數(shù)原型,包括函數(shù)名、要傳送過來的參數(shù)表以及它所返回的函數(shù)值的類型。程序員和編譯系統(tǒng)從這個(gè)語句可了解到:函數(shù)名為square,函數(shù)需要一個(gè)long型參數(shù),并將返回一個(gè)long型值。

(2)語句“result=square(in_num);”調(diào)用square()函數(shù)并將變量in_num作為實(shí)參傳送給它。該函數(shù)的返回值賦予變量result。注意in_num和result為long型變量,以便與函數(shù)原型相匹配。

(3)longsquare(longx)開始函數(shù)定義,函數(shù)定義的首部(即第一行)給出函數(shù)的返回類型、函數(shù)名和形參描述。圍在大括號(hào)中的是函數(shù)體,其中包括函數(shù)內(nèi)部的一些說明和變量定義以及函數(shù)在被調(diào)用時(shí)要執(zhí)行的語句。

(4)函數(shù)以一個(gè)return語句終結(jié),return語句將一個(gè)值傳回調(diào)用程序并結(jié)束函數(shù)的調(diào)用。本例中,返回變量x_square的值。比較square()函數(shù)與main()函數(shù)的結(jié)構(gòu)可知它們是類似的,注意main()雖是一個(gè)函數(shù),但不可被調(diào)用。本書已用過的printf()和scanf()函數(shù)是庫函數(shù),與用戶定義函數(shù)不同,但用戶在調(diào)用時(shí)的操作是相似的。由此可總結(jié),C程序中的函數(shù)具有以下特性:

(1)每個(gè)函數(shù)都有惟一的名字(如square),用這個(gè)名字,程序可以轉(zhuǎn)去執(zhí)行該函數(shù)所包括的語句,這種操作稱為調(diào)用函數(shù)。一個(gè)函數(shù)能被另一個(gè)函數(shù)調(diào)用,如在main()函數(shù)中調(diào)用square()函數(shù),但main()函數(shù)不可被其它任何函數(shù)調(diào)用。

(2)一個(gè)函數(shù)執(zhí)行一個(gè)特定的任務(wù),此任務(wù)是程序必須完成的全部操作中一個(gè)獨(dú)立的操作行為,如將一行正文傳送到打印機(jī),將一個(gè)數(shù)組按數(shù)值順序排列,求一個(gè)立方根等等。

(3)函數(shù)的定義是獨(dú)立的、封閉的。一個(gè)函數(shù)的定義應(yīng)該不受程序其它部分的干預(yù),也應(yīng)不干預(yù)其它部分而完成自身的任務(wù)。

(4)函數(shù)能給調(diào)用程序返回一個(gè)值。在程序調(diào)用一個(gè)函數(shù)時(shí),此函數(shù)所包含的語句便會(huì)執(zhí)行,必要時(shí),還能將信息傳回調(diào)用程序。

C程序中的函數(shù),只有在被調(diào)用時(shí),其中的語句才能執(zhí)行。程序在調(diào)用一個(gè)函數(shù)時(shí),可用一個(gè)或多個(gè)實(shí)參將信息傳送到函數(shù),實(shí)參往往是函數(shù)在執(zhí)行任務(wù)時(shí)所需的數(shù)據(jù)。函數(shù)中的語句執(zhí)行后,就完成了指定的任務(wù)。當(dāng)函數(shù)的語句全部完成后,程序返回到該函數(shù)被調(diào)用處,函數(shù)也能以返回值的形式將信息傳送回程序。7.2函數(shù)的定義和聲明7.2.1函數(shù)的定義函數(shù)的定義就是對(duì)函數(shù)所要完成功能的操作進(jìn)行描述的過程。它一般包括函數(shù)名的命名和類型說明、形式參數(shù)的類型說明、必要的變量定義、操作語句等等。下面首先看一個(gè)函數(shù)定義的實(shí)例,然后給出函數(shù)定義的一般形式。

例7.2

計(jì)算x的n次方,x=2,-3;n=1,2,…,9。分析:根據(jù)題意,x有兩個(gè)取值,每個(gè)x值對(duì)應(yīng)于9個(gè)n值,因此程序要計(jì)算18次x的冪值。所以最好將計(jì)算x的冪定義成函數(shù),函數(shù)名為power,參數(shù)為x和n。main()函數(shù)每次用不同的x和n值調(diào)用power()函數(shù),并輸出計(jì)算結(jié)果。程序如下:

#include<stdio.h>

intmain(void)/*測(cè)試power()函數(shù)*/

{

inti;

doublepower(int,int);/*函數(shù)聲明*/

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

printf(“power(2,%d)=%8.4f,power(-3,%d)

=%11.4f\n“,i,power(2,i),i,

power(-3,i));

return0;

}

doublepower(intx,intn)/*函數(shù)首部*/

{

inti;/*說明部分*/

doublep;

p=1;/*執(zhí)行部分*/

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

p*=x;

return(p);/*返回p值*/

}

輸出:

power(2,1)=2.0000,power(-3,1)=-3.0000power(2,2)=4.0000,power(-3,2)=9.0000power(2,3)=8.0000,power(-3,3)=-27.0000power(2,4)=16.0000,power(-3,4)=81.0000power(2,5)=32.0000,power(-3,5)=-243.0000power(2,6)=64.0000,power(-3,6)=729.0000power(2,7)=128.0000,power(-3,7)=-2187.0000power(2,8)=256.0000,power(-3,8)=

6561.0000power(2,9)=512.0000,power(-3,9)=-19683.0000函數(shù)名power是一個(gè)標(biāo)識(shí)符,power()函數(shù)具有double類型的返回值,它有兩個(gè)int類型的參數(shù)x和n。{}括起來的部分是函數(shù)體,其中的說明部分“inti;doublep;”說明i、p是在power()函數(shù)內(nèi)部使用的局部變量。執(zhí)行部分的“return(p);”語句將表達(dá)式p的值返回給main()函數(shù)的調(diào)用處,p的值就是power()函數(shù)的返回值(簡稱函數(shù)值)。函數(shù)定義的一般形式為:存儲(chǔ)類型標(biāo)識(shí)符類型標(biāo)識(shí)符函數(shù)名(形式參數(shù)表列及類型說明)

{說明部分語句部分}函數(shù)定義由函數(shù)首部和函數(shù)體兩部分組成。函數(shù)首部即定義一個(gè)函數(shù)時(shí)的第一行,包括存儲(chǔ)類型標(biāo)識(shí)符、類型標(biāo)識(shí)符、函數(shù)名和由()括起來的參數(shù)表;{}部分稱為函數(shù)體,語法上是一個(gè)復(fù)合語句。各部分說明如下:

1)存儲(chǔ)類型標(biāo)識(shí)符存儲(chǔ)類型標(biāo)識(shí)符說明函數(shù)的存儲(chǔ)類型,它規(guī)定了函數(shù)可被調(diào)用的范圍??捎糜诤瘮?shù)的存儲(chǔ)類型標(biāo)識(shí)符有static和extern,指定為static的函數(shù)為靜態(tài)函數(shù),靜態(tài)函數(shù)只能由和它在同一文件中定義的函數(shù)調(diào)用;不指定存儲(chǔ)類型標(biāo)識(shí)符時(shí)為缺省的存儲(chǔ)類型extern,缺省或指定為extern存儲(chǔ)類型的函數(shù)是外部函數(shù),例如,例7.2中的power()函數(shù)是外部函數(shù)。

2)類型標(biāo)識(shí)符

C程序中定義的函數(shù)可以什么也不返回而只完成某項(xiàng)工作。無返回值的函數(shù),類型標(biāo)識(shí)符為void,又稱為“空類型函數(shù)”,即此函數(shù)不向主調(diào)函數(shù)返回值,主調(diào)函數(shù)也禁止使用此函數(shù)的返回值。

C程序中定義的函數(shù)也可以返回一個(gè)值,這時(shí),類型標(biāo)識(shí)符說明函數(shù)返回值的數(shù)據(jù)類型(常簡稱為“函數(shù)值的類型”或“函數(shù)的類型”),例如,例7.2中的power()函數(shù)是一個(gè)double類型的函數(shù),main()是int類型的函數(shù)。函數(shù)的類型可以為任何基本類型、結(jié)構(gòu)體類型。還可以定義返回值為指針的函數(shù),但不能定義返回?cái)?shù)組的函數(shù)。int型函數(shù)定義時(shí)可以省略類型標(biāo)識(shí)符int,因?yàn)閕nt是有返回值函數(shù)的缺省類型(提倡明確指出int)。

3)函數(shù)名函數(shù)名是一個(gè)標(biāo)識(shí)符,一個(gè)程序中除主函數(shù)main()外,其余函數(shù)的名字可以任意取,最好取有助于記憶的名字。考慮到與外部聯(lián)接的需要,函數(shù)名一般不要超過6個(gè)字符長,如max()、power()和factor()等。外部函數(shù)的名字要作用于整個(gè)程序,因而外部函數(shù)相互之間不能同名。靜態(tài)函數(shù)可以和外部函數(shù)同名,但同一文件中的函數(shù)不能同名。

4)參數(shù)表函數(shù)定義中的參數(shù)表說明函數(shù)參數(shù)的名稱、類型和數(shù)目。參數(shù)表由零個(gè)或多個(gè)參數(shù)說明組成,如果函數(shù)沒有參數(shù),可只寫一對(duì)括號(hào)(此為函數(shù)標(biāo)志,不可省略),但最好將參數(shù)表指定為void。有多個(gè)參數(shù)時(shí),多個(gè)參數(shù)之間用逗號(hào)隔開。函數(shù)定義中的參數(shù)表習(xí)慣上稱為形參表。形參說明的一般形式為:

類型標(biāo)識(shí)符形參名每個(gè)類型標(biāo)識(shí)符對(duì)應(yīng)于一個(gè)形參名,當(dāng)有多個(gè)形參時(shí),相互間用逗號(hào)隔開。例如,例7.2中的main()函數(shù)沒有參數(shù),形參表為void;power()函數(shù)有兩個(gè)形參,其形參表示為:intx,intn形參是局部變量,僅在本函數(shù)內(nèi)有定義,任何其它函數(shù)不能使用它們進(jìn)行存取。

5)函數(shù)體和函數(shù)返回值函數(shù)定義中最外層{}括起來的部分稱為函數(shù)體,函數(shù)體由說明部分和執(zhí)行部分組成。說明部分是局部說明,執(zhí)行部分是可執(zhí)行語句的序列,完成本函數(shù)要完成的具體任務(wù)。局部說明中說明的變量和函數(shù)其有效范圍局限于該函數(shù)內(nèi)部,同形參一樣,不能由其它任何函數(shù)存取(或調(diào)用)。例7.2的main()函數(shù)中,變量i是main()函數(shù)的局部變量,power()函數(shù)中的變量i和p是power()函數(shù)的局部變量。其中main()和power()使用了同名變量i,但它們各自有自己的存儲(chǔ)單元,是完全不同的兩個(gè)變量。函數(shù)體語法上是一個(gè)復(fù)合語句,它可以沒有說明部分而只有執(zhí)行部分,也可以兩者都沒有。因此,最簡單的合法函數(shù)是形參表為空(void)且函數(shù)體也為空的函數(shù)(稱為啞函數(shù))。例如:

voiddummy(void){}dummy()函數(shù)被調(diào)用時(shí),它不執(zhí)行任何操作,僅在調(diào)用程序的流程控制中占有一個(gè)位置。當(dāng)調(diào)用程序的功能需要擴(kuò)充時(shí),可編寫一個(gè)具有新功能的函數(shù),并用對(duì)新函數(shù)的調(diào)用取代相應(yīng)的啞函數(shù)調(diào)用。這在程序開發(fā)的初級(jí)階段是非常有用的,讀者可參考配套的《〈C程序設(shè)計(jì)〉學(xué)習(xí)指導(dǎo)(第二版)》書中的應(yīng)用系統(tǒng)設(shè)計(jì)題來體會(huì)。

void類型函數(shù)不含return或含不帶表達(dá)式的return語句。有返回值的函數(shù)必須至少包含一個(gè)帶表達(dá)式的return語句,表示函數(shù)調(diào)用至此結(jié)束,返回到主調(diào)函數(shù)的函數(shù)調(diào)用處。

return表達(dá)式;或

return(表達(dá)式);表達(dá)式的值就是函數(shù)的返回值。對(duì)于基本類型,表達(dá)式的類型和函數(shù)的類型不相同時(shí)表達(dá)式的值自動(dòng)轉(zhuǎn)換為函數(shù)的類型;對(duì)于指針,表達(dá)式的類型和函數(shù)的類型不相同時(shí),須使用類型強(qiáng)制符將表達(dá)式的值轉(zhuǎn)換為函數(shù)的類型;對(duì)于結(jié)構(gòu)體,表達(dá)式值的類型與函數(shù)定義的類型必須相同。例如,可以將power()函數(shù)定義為:

doublepower(intx,intn)

{

inti;longp;

return(p);

}其中,“return(p)”將表達(dá)式p的值作為power()函數(shù)的返回值。p被自動(dòng)轉(zhuǎn)換成double類型。7.2.2函數(shù)的聲明(函數(shù)原型)

C語言允許函數(shù)先調(diào)用后定義,或被調(diào)用函數(shù)在其它文件中定義。對(duì)于此種情況之一的非int型函數(shù),必須在調(diào)用函數(shù)之前作函數(shù)聲明,其目的是指出被調(diào)用函數(shù)的類型和參數(shù)的類型,否則編譯程序認(rèn)為被調(diào)用函數(shù)為int類型。但是我們需要注意最新的C++標(biāo)準(zhǔn)已不再支持默認(rèn)的int()函數(shù)和變量,因此,建議大家在定義變量和函數(shù)時(shí),不要省略類型標(biāo)識(shí)符。函數(shù)聲明的一般形式為:存儲(chǔ)類型標(biāo)識(shí)符類型標(biāo)識(shí)符函數(shù)名(形參表);外部函數(shù)聲明時(shí)可指定extern或存儲(chǔ)類型標(biāo)識(shí)符缺省,靜態(tài)函數(shù)聲明時(shí)必須指定static;參數(shù)表可以只列出參數(shù)的類型名而不需給出參數(shù)名。例如:

doublepower(int,int);和doublepower(intx,intn);和doublepower(inta,intm);都是等價(jià)的。power()函數(shù)是double類型,它有兩個(gè)int參數(shù)。聲明時(shí)給出的參數(shù)名x、n被編譯忽略,因?yàn)閰?shù)的存儲(chǔ)分配是在函數(shù)被調(diào)用時(shí)進(jìn)行的。對(duì)于無參數(shù)表的函數(shù),聲明時(shí)參數(shù)表應(yīng)指定為void。函數(shù)聲明可位于調(diào)用函數(shù)體內(nèi)或函數(shù)體外(一般位于程序開頭部分)。在函數(shù)體外聲明的函數(shù)可在聲明之后直至該源文件結(jié)束的任何函數(shù)中調(diào)用,在函數(shù)體內(nèi)聲明的函數(shù)只能在聲明所在的函數(shù)體內(nèi)調(diào)用。在函數(shù)體中聲明時(shí),還可以寫成“doublepower();”這種形式,在早期的C語言書籍中多采用該形式;而目前流行的形式是采用函數(shù)體外部聲明。例如,在例7.2中main()函數(shù)調(diào)用了power()函數(shù),power()函數(shù)的定義在main()函數(shù)的定義之后,且類型為非int類型,所以在main()的說明部分要對(duì)power()函數(shù)作聲明:

doublepower(int,int);也可以在main()函數(shù)外面聲明:

doublepower(int,int);

intmain(void)

{

inti;

}帶參數(shù)表的函數(shù)聲明稱為函數(shù)原型。標(biāo)準(zhǔn)庫函數(shù)的原型在系統(tǒng)提供的相應(yīng)頭文件中,因此,程序中調(diào)用標(biāo)準(zhǔn)庫函數(shù)時(shí),只需用#include預(yù)處理控制包含所需的頭文件,而不需寫函數(shù)聲明。實(shí)際上,不管是否必須,對(duì)所有被調(diào)用函數(shù)均進(jìn)行聲明是較好的編程習(xí)慣,既符合現(xiàn)代程序設(shè)計(jì)風(fēng)格,又方便了程序的檢查和閱讀。7.3函數(shù)的調(diào)用一個(gè)函數(shù)可以被其它函數(shù)多次調(diào)用,每次調(diào)用時(shí)可以處理不同的數(shù)據(jù),因此函數(shù)是對(duì)不同數(shù)據(jù)進(jìn)行相同處理的一種通用的程序形式。通常將函數(shù)定義時(shí)在參數(shù)表列出的參數(shù)稱為形式參數(shù),簡稱形參。形參是函數(shù)要處理的數(shù)據(jù)名稱(形式上的變量),在函數(shù)定義時(shí)并未開辟相應(yīng)的存儲(chǔ)單元,只有到函數(shù)調(diào)用時(shí),系統(tǒng)才為形式參數(shù)分配與其類型長度相同的存儲(chǔ)單元,并將實(shí)際要處理的參數(shù)送到形參對(duì)應(yīng)的存儲(chǔ)單元。每次調(diào)用時(shí),使用不同的實(shí)際數(shù)據(jù)從而實(shí)現(xiàn)對(duì)不同數(shù)據(jù)的相同處理。調(diào)用時(shí)被送到形參單元的實(shí)際數(shù)據(jù)通常稱為實(shí)際參數(shù),簡稱實(shí)參。形參是變量,實(shí)參是形參在每次調(diào)用時(shí)得到的值。7.3.1函數(shù)調(diào)用的方式和條件函數(shù)調(diào)用的一般形式為:函數(shù)名(實(shí)參1,實(shí)參2,…,實(shí)參n)

()部分稱為實(shí)參表列,實(shí)參可以是常量、變量或表達(dá)式,有多個(gè)實(shí)參時(shí),相互間用逗號(hào)隔開。實(shí)參和形參應(yīng)在數(shù)目、次序和類型上一致。對(duì)于無參數(shù)的函數(shù),調(diào)用時(shí)實(shí)參表為空,但()不能省。函數(shù)調(diào)用在程序中起一個(gè)表達(dá)式或一個(gè)語句的作用。對(duì)于有返回值的函數(shù),函數(shù)調(diào)用一般作為表達(dá)式出現(xiàn),即凡程序中允許出現(xiàn)表達(dá)式的位置上均可出現(xiàn)函數(shù)調(diào)用;也可作為語句(即表達(dá)式語句)出現(xiàn)。對(duì)于無返回值函數(shù)的調(diào)用,只能以語句形式出現(xiàn)。例如:

(1)getch();

getch()函數(shù)調(diào)用作為語句出現(xiàn)。

(2)c=getchar();

getchar()函數(shù)調(diào)用作為表達(dá)式出現(xiàn)(賦值表達(dá)式的右操作數(shù))。

(3)while(putchar(getche())!=′?′);

getche()函數(shù)調(diào)用作為putchar()函數(shù)調(diào)用的實(shí)參(表達(dá)式)出現(xiàn),putchar()函數(shù)調(diào)用作為關(guān)系表達(dá)式的左操作數(shù)(表達(dá)式)出現(xiàn)。

(4)while((c=getch())!=′?′)

putchar(c);

putchar()函數(shù)調(diào)用作為while語句的循環(huán)體(語句)出現(xiàn)。函數(shù)調(diào)用的一般過程為:

(1)主調(diào)函數(shù)在執(zhí)行過程中,一旦遇到函數(shù)調(diào)用,系統(tǒng)首先計(jì)算實(shí)參表達(dá)式的值并為每個(gè)形參分配存儲(chǔ)單元,然后把實(shí)參值復(fù)制到(送到或存入)對(duì)應(yīng)形參的存儲(chǔ)單元中。實(shí)參與形參按位置一一對(duì)應(yīng)。

(2)將控制轉(zhuǎn)移到被調(diào)用的函數(shù),執(zhí)行其函數(shù)體內(nèi)的語句。

(3)當(dāng)執(zhí)行return語句或到達(dá)函數(shù)體末尾時(shí),控制返回到調(diào)用處,如果有返回值,同時(shí)回送一個(gè)值。然后從函數(shù)調(diào)用點(diǎn)繼續(xù)執(zhí)行主調(diào)函數(shù)后面的操作。除了正確地編寫函數(shù)的定義及調(diào)用語句,要想成功地調(diào)用某個(gè)函數(shù)還必須滿足下列三個(gè)條件之一:

(1)被調(diào)用函數(shù)的定義出現(xiàn)在主調(diào)函數(shù)的定義之前。

(2)在主調(diào)函數(shù)中或主調(diào)函數(shù)之前的外部對(duì)被調(diào)用函數(shù)進(jìn)行聲明。

(3)被調(diào)用函數(shù)為標(biāo)準(zhǔn)函數(shù)時(shí),在函數(shù)調(diào)用前已包含了相應(yīng)的頭文件。7.3.2形參與實(shí)參的數(shù)值傳遞函數(shù)調(diào)用時(shí)將實(shí)參傳送給形參稱為參數(shù)傳遞。C語言中,參數(shù)的傳遞方式是“單向值傳遞”,形參和實(shí)參變量各自有不同的存儲(chǔ)單元,被調(diào)用函數(shù)中形參變量值的變化不會(huì)影響實(shí)參變量的值。

例7.3

形參與實(shí)參的數(shù)值傳遞。

#include<stdio.h>

voidswap(intx,inty)

{

intz;

z=x;x=y;y=z;

}

voidmain()

{

inta,b;

a=10;b=20;

swap(a,b);

printf("a=%d\tb=%d\n",a,b);

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

a=10b=20可以看到,在調(diào)用swap()函數(shù)時(shí),實(shí)參a和b的值是10和20。進(jìn)入被調(diào)函數(shù)時(shí),先開辟形參單元x和y,再將a和b的值分別傳遞給形參變量x和y(如圖7.2(a)所示),執(zhí)行swap()函數(shù)使x和y的值進(jìn)行了交換,但交換的結(jié)果并不會(huì)使實(shí)參變量a和b交換,所以a和b的值仍然為10和20(如圖7.2(b)所示)。圖7.2swap()函數(shù)的傳遞因此,在執(zhí)行一個(gè)被調(diào)用函數(shù)時(shí),形參的值如果發(fā)生改變,并不會(huì)改變主調(diào)函數(shù)實(shí)參的值。實(shí)際上當(dāng)實(shí)參為常量或表達(dá)式時(shí),這一點(diǎn)更容易理解。在參數(shù)傳遞時(shí)有三個(gè)問題需要注意:

(1)實(shí)參與形參的一致性,實(shí)參的數(shù)目和類型應(yīng)該與形參保持一致。如果參數(shù)的數(shù)目不一致或類型不一致,則調(diào)用的效果不確定,這是程序得不到正確結(jié)果的原因之一。

(2)C語言中可以定義參數(shù)數(shù)目可變的函數(shù)。定義這種函數(shù)時(shí),要求至少要給出一個(gè)形參,在列出的最后一個(gè)形參后面用逗號(hào)后再跟三個(gè)點(diǎn),即“,…”來聲明該函數(shù)的參數(shù)數(shù)目可變。調(diào)用參數(shù)數(shù)目可變的函數(shù)時(shí),實(shí)參的數(shù)目不能少于(可以多于)形參表中列出的形參的數(shù)目,即最后一個(gè)逗號(hào)之前的形參的數(shù)目。實(shí)參在類型和次序上同樣應(yīng)與形參一致。printf()和scanf()函數(shù)就是C中最常用的參數(shù)數(shù)目可變的函數(shù)。例如,printf()函數(shù)調(diào)用的一般形式為:

printf(格式字符串,參數(shù)1,參數(shù)2,…);其中第一個(gè)參數(shù)(格式字符串)是必需的,調(diào)用時(shí)系統(tǒng)根據(jù)第一個(gè)參數(shù)中的格式說明項(xiàng)的數(shù)目和格式說明字符來確定其余參數(shù)的數(shù)目和類型,例如:

printf("x=%dy=%f",x,y);因?yàn)榈谝粋€(gè)參數(shù)中有兩個(gè)格式說明項(xiàng),分別為%d和%f,所以確定printf()在本次調(diào)用中還有兩個(gè)輸出參數(shù),分別為整數(shù)和浮點(diǎn)數(shù)。

(3)函數(shù)調(diào)用時(shí),每一實(shí)參為一表達(dá)式,實(shí)參與實(shí)參間的逗號(hào)是分隔符,不是順序求值運(yùn)算符,它不保證參數(shù)的求值順序按從左至右進(jìn)行,參數(shù)的求值順序由具體系統(tǒng)確定。多數(shù)編譯程序在計(jì)算參數(shù)值時(shí)按從右至左的順序進(jìn)行。例如在TurboC中運(yùn)行下列程序。

例7.4

參數(shù)的求值順序。

#include<stdio.h>

voidmain(void)

{

intx=0;

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

printf("x++=%dx++=%d\n",x++,x++);

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

}執(zhí)行時(shí)輸出:

x=0

x++=1x++=0

x=2在該程序的第二個(gè)printf語句中,右邊的x++先求值,左邊的x++后求值,因此右邊的x++的輸出為0,左邊的為1。7.3.3函數(shù)的返回值當(dāng)函數(shù)為有返回值的函數(shù)時(shí),在函數(shù)中要用return后跟一個(gè)C表達(dá)式構(gòu)成的語句完成。在執(zhí)行到返回語句時(shí),表達(dá)式求值,并將其值返回到調(diào)用程序,函數(shù)的返回值就是該表達(dá)式的值。例7.5

函數(shù)的返回值。

#include<stdio.h>

floatmax(floatx,floaty)

{

if(x>=y)return(x);

elsereturn(y);

}

voidmain()

{

floata,b,c;

scanf("%f%f",&a,&b);

c=max(a,b);

printf("max=%5.2f\n",c);

}還可以寫成:

(1)floatmax(floatx,floaty)

{

if(x>=y)

returnx;

returny;

}

(2)floatmax(floatx,floaty)

{

returnx>y?x:y;

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

2.55.6

max=5.00在max()函數(shù)被調(diào)用時(shí),函數(shù)體中的語句執(zhí)行到return語句,return終止函數(shù)的執(zhí)行并將y的值返回給調(diào)用程序,return關(guān)鍵字之后的表達(dá)式可以是任意的C表達(dá)式。如例7.5所示,函數(shù)中可包含多個(gè)return語句??梢钥吹胶瘮?shù)的類型與return語句中的表達(dá)式的值不一致,此時(shí)以函數(shù)類型為準(zhǔn)。對(duì)數(shù)值型數(shù)據(jù),可以自動(dòng)進(jìn)行類型轉(zhuǎn)換,即函數(shù)類型決定返回值的類型。一般情況下提倡函數(shù)類型與return語句中的表達(dá)式的值一致。若函數(shù)不需要返回值時(shí),可以用void定義函數(shù)類型,此時(shí)函數(shù)中不可有帶表達(dá)式的return語句。如果函數(shù)的類型不是void,即使函數(shù)中沒有return語句,函數(shù)也有返回值,只是返回值為不確定的值。需要說明的是,現(xiàn)代的C++語法要求越來越嚴(yán)格,新的編譯系統(tǒng)甚至已經(jīng)不支持返回值為int型的函數(shù)省略“int”返回類型的寫法,即認(rèn)為max(intx,inty)這種形式是非法,必須寫成intmax(intx,inty)。因此,我們?cè)诰帉懞瘮?shù)時(shí),必須寫出函數(shù)的返回值類型,當(dāng)有返回值時(shí),一定要有return語句。在C語言中,main()函數(shù)是一個(gè)特殊的函數(shù),它由操作系統(tǒng)調(diào)用,然后返回到操作系統(tǒng)。因此,一般來講,main()函數(shù)是有返回值的,其返回值為int類型。我們?cè)诰帉懗绦驎r(shí),如果main()函數(shù)的形式是intmain(){…},函數(shù)的結(jié)尾必須有“return1;”之類的語句;而經(jīng)常地,我們也把main()函數(shù)寫成voidmain(){…}這種形式,表示沒有給操作系統(tǒng)返回任何值,無需寫“return1;”這樣的語句,從而顯得更簡潔。在本書中,我們多用voidmain()這樣的形式。7.4函數(shù)的嵌套與遞歸當(dāng)一個(gè)函數(shù)作為被調(diào)用函數(shù)時(shí),它也可以作為另一個(gè)函數(shù)的主調(diào)函數(shù),而它的被調(diào)用函數(shù)又可以調(diào)用其它函數(shù),這就是函數(shù)的嵌套調(diào)用。當(dāng)一個(gè)函數(shù)直接或間接地調(diào)用它自身時(shí),稱為函數(shù)的遞歸。C語言規(guī)定任何函數(shù)都可以調(diào)用其它函數(shù),且除了main()函數(shù)以外的任何函數(shù)都可以作為其它函數(shù)的被調(diào)用函數(shù)。7.4.1函數(shù)的嵌套調(diào)用函數(shù)的定義是不允許嵌套的,但調(diào)用是可以的。實(shí)際上,在稍微復(fù)雜一點(diǎn)的程序中,嵌套調(diào)用是常常發(fā)生的。

例7.6

用牛頓迭代法求根。方程為ax3+bx2+cx+d=0,系數(shù)由主函數(shù)輸入。求x在1附近的一個(gè)根。求出根后由主函數(shù)輸出。

分析:牛頓迭代法的公式是x=x0-(f(x)/f′(x)),假設(shè)迭代到|x-x0|≤10-5時(shí)結(jié)束。求解時(shí),先計(jì)算函數(shù)f(x0),再計(jì)算f′(x0)和x的值,利用循環(huán)精確到六位小數(shù)。程序如下:

#include<stdio.h>

#include<math.h>

doublef(doublea,doubleb,doublec,doubled,doublex)

{

returna*x*x*x+b*x*x+c*x+d;

}

doubledf(doublea,doubleb,doublec,doublex)

/*f(x)的微分函數(shù)*/

{

return3*a*x*x+2*b*x+c;

}

doubleNewton(doublea,doubleb,doublec,doubled,doublex0,doubleeps)

{

doublex=x0,t;

do

{

t=f(a,b,c,d,x)/df(a,b,c,x);

x=x0-t;

x0=x;

}while(fabs(t)>=eps);

returnx;

}

voidmain()

{

doublea,b,c,d,eps;

printf("\nInputa,b,c,d,eps\n");

scanf("%lf%lf%lf%lf%lf",&a,&b,&c,&d,&eps);

printf(“\nx=%10.7f\n”,Newton(a,b,c,d,1,eps));

}

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

Inputa,b,c,d,eps

12341e-6↙

x=-1.6506292可以看到在程序運(yùn)行時(shí),函數(shù)main()調(diào)用了函數(shù)Newton(),而函數(shù)Newton()又調(diào)用了函數(shù)f()和df(),這樣就形成了函數(shù)的嵌套調(diào)用。7.4.2函數(shù)的遞歸及條件遞歸是一種特殊的解決問題的方法,要用遞歸解決問題,應(yīng)滿足兩個(gè)條件:

(1)函數(shù)直接或間接地調(diào)用它本身;

(2)應(yīng)有使遞歸結(jié)束的條件。例7.7

用遞歸方法求n!。

分析:求n!可以用遞推方法,即從1開始,乘2,再乘3……一直乘到n;也可以用遞歸方法實(shí)現(xiàn),即5!等于4!×5,而4!=3!×4,…,1!=1??梢杂孟旅娴倪f歸公式表示:程序如下:

#include<stdio.h>

floatfac(intn)

{

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

return1;

returnn*fac(n-1);

}

voidmain()

{

intn;

floaty;

printf("inputaintegernumber:");

scanf("%d",&n);

y=fac(n);

printf("%d!=%15.0f",n,y);

}

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

inputaintegernumber:10↙

10?。?62800例7.8

求Fibonacci數(shù)列:1,1,2,3,5,8,……即程序如下:

#include<stdio.h>

doublefib(intn)

{

if(n==1||n==2)

return1;

returnfib(n-1)+fib(n-2);

}

voidmain()

{

intn;

printf("n=");

scanf("%d",&n);

printf("%lf",fib(n));

}思考:main()函數(shù)可否進(jìn)行遞歸調(diào)用?7.5變量的存儲(chǔ)類別

C語言的數(shù)據(jù)有兩種屬性:數(shù)據(jù)類型和存儲(chǔ)類型。因此完整的變量說明的一般形式為:存儲(chǔ)類型標(biāo)識(shí)符類型標(biāo)識(shí)符變量名;其中類型標(biāo)識(shí)符說明變量的數(shù)據(jù)類型,在前面章節(jié)已經(jīng)講述了數(shù)據(jù)類型,如整型、實(shí)型等。本節(jié)講述數(shù)據(jù)的存儲(chǔ)類型,C語言的數(shù)據(jù)有四種存儲(chǔ)類型,分別由四個(gè)關(guān)鍵字(稱為存儲(chǔ)類型標(biāo)識(shí)符)表示:auto(自動(dòng))、extern(全局)、static(靜態(tài))和register(寄存器)。7.5.1變量的作用域和生存期變量的作用域是指一個(gè)范圍,這個(gè)范圍內(nèi)程序的各個(gè)部分都可訪問該變量。換句話說,變量在這個(gè)范圍內(nèi)是可使用的或“可見的”。本書中,“可見的”可以指所有的C數(shù)據(jù)類型,即簡單變量、數(shù)組、結(jié)構(gòu)、指針等等,也可以指由const關(guān)鍵字定義的常量。例7.9

變量的作用域。

#include<stdio.h>

intx=999;/*定義全局變量x*/

voidprint_value(void);

voidmain()

{

printf("%d\n",x);

print_value();

}

voidprint_value(void)

{

printf("%d\n",x);

}輸出:

999999在程序的最開始處定義了變量x,在main()函數(shù)中用printf()顯示x的值,然后調(diào)用函數(shù)print_value()再次顯示x的值??煽吹絰并未作為一個(gè)實(shí)參傳送到函數(shù)print_value(),而是直接作為printf()中的一個(gè)實(shí)參。這是因?yàn)樽兞縳的作用域包括了main()函數(shù)和print_value()函數(shù)。現(xiàn)對(duì)程序做一點(diǎn)小修改,將變量x的定義移到main()之內(nèi),則新的源程序如下:

例7.10

變量的作用域。

#include<stdio.h>

voidprint_value(void);

voidmain()

{

intx=999;/*定義局部變量x*/

printf("%d\n",x);

print_value();

}

voidprint_value(void)

{

printf("%d\n",x);

}上述程序在編譯時(shí)將會(huì)提示在第11行有錯(cuò)誤——未定義變量x。這是因?yàn)樽兞縳的定義位于main()函數(shù)內(nèi),它的作用域也只限于main()內(nèi),在print_value()函數(shù)內(nèi),變量x未被定義或者說是不可見的。上面兩個(gè)例子的惟一區(qū)別在于變量x定義的位置,將x的定義移位,其作用域發(fā)生了變化。在例7.9中,x定義在函數(shù)的外部并位于文件的最前面,其作用域?yàn)檎麄€(gè)程序,在main()函數(shù)與printf_value()函數(shù)內(nèi)都是可訪問的。在例7.10中,x定義在main()函數(shù)的內(nèi)部,其作用域局限于main()函數(shù)中。為了理解變量作用域的重要性,可復(fù)習(xí)一下在本章前面所講的結(jié)構(gòu)化程序設(shè)計(jì)思想。結(jié)構(gòu)化方法將程序劃分為分別執(zhí)行特定任務(wù)的獨(dú)立函數(shù),為了具備真正的獨(dú)立性,每個(gè)函數(shù)中的變量必須隔離以避免來自其它函數(shù)的干擾。函數(shù)之間的完全數(shù)據(jù)隔離并不總是必要的。但是,通過指定變量的作用域,程序員可更進(jìn)一步地控制數(shù)據(jù)隔離的程度。當(dāng)使用一個(gè)變量時(shí),需要首先在內(nèi)存中給這個(gè)變量開辟相應(yīng)的存儲(chǔ)單元,這時(shí)可以說這個(gè)變量存在了,或說它處于生存期內(nèi)。如果這個(gè)變量所占用的內(nèi)存單元被釋放,那么這個(gè)變量就不存在了,或說在生存期之外。所以生存期指變量在內(nèi)存中占用內(nèi)存單元的時(shí)間。當(dāng)一個(gè)程序運(yùn)行時(shí),程序中所包含的變量并不一定在程序運(yùn)行的整個(gè)過程中都占用內(nèi)存單元,往往是在需要時(shí)占用內(nèi)存,而在使用結(jié)束后釋放內(nèi)存,這樣做可以提高內(nèi)存單元的使用效率,所以就產(chǎn)生了變量的生存期問題。7.5.2動(dòng)態(tài)存儲(chǔ)和靜態(tài)存儲(chǔ)內(nèi)存中供用戶使用的存儲(chǔ)空間可分為程序區(qū)、動(dòng)態(tài)存儲(chǔ)區(qū)和靜態(tài)存儲(chǔ)區(qū),如圖7.3所示。程序區(qū)用來存放程序代碼,動(dòng)態(tài)存儲(chǔ)區(qū)和靜態(tài)存儲(chǔ)區(qū)用來存放數(shù)據(jù),即數(shù)據(jù)與處理數(shù)據(jù)的程序是分離的,這是面向過程的程序設(shè)計(jì)方法的特點(diǎn)。靜態(tài)存儲(chǔ)區(qū)即全局?jǐn)?shù)據(jù)區(qū),用來存放程序的全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù)。圖7.3程序的內(nèi)存使用動(dòng)態(tài)存儲(chǔ)區(qū)又分為堆區(qū)和棧區(qū)。堆區(qū)用來存放程序的動(dòng)態(tài)數(shù)據(jù);棧區(qū)用來存放程序的局部數(shù)據(jù),即各個(gè)函數(shù)中的數(shù)據(jù)。動(dòng)態(tài)存儲(chǔ)和靜態(tài)存儲(chǔ)是指C對(duì)數(shù)據(jù)存儲(chǔ)的兩種方式。動(dòng)態(tài)存儲(chǔ)是指存儲(chǔ)一些數(shù)據(jù)的存儲(chǔ)單元可在程序運(yùn)行的不同時(shí)間分配給不同的數(shù)據(jù),而靜態(tài)存儲(chǔ)是指存儲(chǔ)單元在程序運(yùn)行的整個(gè)過程中固定地分配給某些數(shù)據(jù)。動(dòng)態(tài)存儲(chǔ)區(qū)中數(shù)據(jù)的生存期一般是程序運(yùn)行中的某個(gè)階段,而靜態(tài)存儲(chǔ)區(qū)中數(shù)據(jù)的生存期為整個(gè)程序運(yùn)行過程。決定數(shù)據(jù)存放在內(nèi)存的哪個(gè)區(qū)域,是由變量定義時(shí)存儲(chǔ)類型標(biāo)識(shí)符和變量定義的位置所決定的。7.5.3局部變量局部變量又稱內(nèi)部變量,是在一個(gè)函數(shù)內(nèi)定義,其作用域限制在所定義的函數(shù)中。main()函數(shù)中定義的變量也是局部變量,像例7.10中的變量x那樣,該變量在main()函數(shù)內(nèi)定義,并像編譯該程序所表明的那樣,該變量僅在main()函數(shù)內(nèi)是可見的,即main()函數(shù)中所定義的局部變量只能在main()函數(shù)內(nèi)使用,不可在其它函數(shù)中使用,而且main()函數(shù)也不可以使用其它函數(shù)所定義的局部變量。函數(shù)的形式參數(shù)被認(rèn)為是局部變量,在函數(shù)被調(diào)用時(shí)才會(huì)在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)中開辟存儲(chǔ)單元,函數(shù)調(diào)用結(jié)束時(shí)與此函數(shù)內(nèi)的其它局部自動(dòng)變量一樣釋放所占有的內(nèi)存單元。局部變量的存儲(chǔ)類型可以通過類型標(biāo)識(shí)符auto和static來規(guī)定。利用auto定義的變量存放在動(dòng)態(tài)存儲(chǔ)區(qū)中,auto可以缺省;利用static定義的變量存放在靜態(tài)存儲(chǔ)區(qū)中。編譯器并不將局部自動(dòng)變量預(yù)置為0。如果一個(gè)局部變量未在定義時(shí)初始化,其值是不確定的或無意義的。程序員必須在局部變量開始使用之前確切地給它們賦值。不同函數(shù)中可以使用相同名字的局部變量,它們代表不同的變量,相互不會(huì)形成干擾。局部變量還可以與全局變量同名,此時(shí)在局部變量的作用域內(nèi),全局變量不起作用。例7.11

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

#include<stdio.h>

inta=1,b=2;/*定義全局變量a、b*/

intmax(inta,intb)/*子函數(shù)中的局部變量a、b*/

{

intc;/*等價(jià)于autointc;*/

c=a>b?a:b;

return(c);

}

voidmain()

{

inta=8;/*定義局部變量a*/

printf("%d",max(a,b));

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

8程序中有兩個(gè)全局變量a、b,在max()函數(shù)中有形參a、b,形參相當(dāng)于局部變量,在函數(shù)調(diào)用時(shí)它們的值來自于實(shí)參的值,與同名全局變量無關(guān)。而在main()函數(shù)中有局部變量a,在函數(shù)中使用的變量a為局部變量,值為8,與同名全局變量無關(guān),使用的變量b為全局變量,值為2。所以max()函數(shù)形參得到的值分別為8和2,最后返回值為8。例7.11中的局部變量定義時(shí)都缺省了存儲(chǔ)類型的說明,這就相當(dāng)于用auto標(biāo)識(shí)符進(jìn)行定義,也就是說定義為局部自動(dòng)變量,在動(dòng)態(tài)存儲(chǔ)區(qū)中存放。局部變量可以用static標(biāo)識(shí)符定義為局部靜態(tài)變量,在靜態(tài)存儲(chǔ)區(qū)中存放。局部靜態(tài)變量的作用域仍然是定義它的函數(shù)內(nèi),但因?yàn)樗庆o態(tài)存儲(chǔ)類型,所以它的生存期為程序運(yùn)行的整個(gè)過程。局部靜態(tài)變量與局部動(dòng)態(tài)變量在使用上也有較大區(qū)別。7.5.4局部靜態(tài)變量的使用局部靜態(tài)變量具有一定的特殊性,它在程序運(yùn)行的整個(gè)過程中都占用內(nèi)存單元,當(dāng)其所在的函數(shù)被調(diào)用時(shí),它才可以被使用,而在函數(shù)調(diào)用結(jié)束后,該變量雖然仍在內(nèi)存中存在,但是不可以被調(diào)用。例7.12

局部靜態(tài)變量的使用。

#include<stdio.h>

voidf()

{

inta,b=3;

staticintc,d=5;

a=3;c=5;

a++;b++;c++;d++;

printf(“%d\t%d\t%d\t%d\n”,a,b,

c,d);

}

voidmain()

{

f();f();

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

4466

4467在函數(shù)f()中,a和b為局部自動(dòng)變量,在每次調(diào)用f()時(shí),a和b都會(huì)重新開辟內(nèi)存單元。對(duì)于變量b來說,也會(huì)重新賦初值,這樣兩次調(diào)用得到的結(jié)果是一樣的。而變量c和d為局部靜態(tài)變量,它們?cè)诔绦蜻\(yùn)行開始時(shí)就獲得了內(nèi)存中的存儲(chǔ)單元,且變量d進(jìn)行了賦初值,而c未賦初值。在調(diào)用函數(shù)f()時(shí),對(duì)變量c進(jìn)行了賦值,對(duì)它們進(jìn)行了自增運(yùn)算后,輸出的結(jié)果都為6。在函數(shù)f()調(diào)用結(jié)束后,它們?nèi)匀淮嬖谟趦?nèi)存中,且它們的值還被保存著。當(dāng)?shù)诙握{(diào)用函數(shù)f()時(shí),它們不會(huì)重新開辟內(nèi)存單元,所以變量d不會(huì)再次賦初值,那么它的值仍然為6,而變量c進(jìn)行了一次賦值操作,它的值被賦為5,變量c和d自增后輸出的結(jié)果分別為6和7。從例7.12中可以清楚地看到局部自動(dòng)變量和局部靜態(tài)變量的區(qū)別,同時(shí)可以看到對(duì)于局部靜態(tài)變量初始化和賦值會(huì)導(dǎo)致不同的結(jié)果。局部靜態(tài)變量的特性可用來優(yōu)化程序,從例7.13中可以看出。例7.13

打印1到5的階乘。

#include<stdio.h>

intfac(intn)

{

staticintf=1;

f*=n;

return(f);

}

voidmain()

{

inti;

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

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

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

1!=1

2!=2

3!=6

4!=24

5!=120在例7.13中,每次調(diào)用函數(shù)fac(),打印出一個(gè)數(shù)的階乘,同時(shí)保留這個(gè)結(jié)果,避免了重復(fù)的運(yùn)算,此時(shí)利用的就是局部靜態(tài)變量的特點(diǎn)。

7.5.5全局變量全局變量(也稱外部變量)是在所有函數(shù)、包括main()函數(shù)之外定義的。全局變量是存放在靜態(tài)存儲(chǔ)區(qū)中的,它的作用域是從全局變量定義之后直到該源文件結(jié)束的所有函數(shù);通過用extern作引用說明,全局變量的作用域可以擴(kuò)大到整個(gè)程序的所有文件。在定義全局變量時(shí)可以使用static存儲(chǔ)類型標(biāo)識(shí)符,它與普通全局變量的區(qū)別在于變量的作用域。普通全局變量不僅對(duì)文件中的所有函數(shù)都是可見的,而且能被其它文件中的函數(shù)所用;而static型的全局變量僅對(duì)其所在文件中定義處之后的函數(shù)是可見的,不能被其它文件使用。這種差別適合于程序源代碼包含在兩個(gè)或多個(gè)文件中的情況。全局變量初始化是在全局變量定義時(shí)進(jìn)行的,且其初始化僅執(zhí)行一次,若無顯式初始化,則由系統(tǒng)自動(dòng)初始化為與變量類型相同的0初值:整型變量初始化為整數(shù)0;

浮點(diǎn)型變量初始化為浮點(diǎn)數(shù)0.0;

字符型變量初始化為空字符′\0′。在有顯式初始化的情況下,初值必須是常量表達(dá)式。全局變量存放在靜態(tài)存儲(chǔ)區(qū)中,全局變量在程序執(zhí)行之前分配存儲(chǔ)單元,在程序運(yùn)行結(jié)束后才被收回。例7.14

輸入以秒為單位的一個(gè)時(shí)間值,將其轉(zhuǎn)化成“時(shí):分:秒”的形式輸出。將轉(zhuǎn)換工作定義成函數(shù)。

#include<stdio.h>

inthh,mm,ss;

voidconvertime(longseconds)

{

hh=seconds/3600;

mm=(seconds-hh*3600L)/60;

ss=seconds-hh*3600L-mm*60;

}

voidmain(void)

{

longseconds;

printf(“hh=%d,mm=%d,ss=%d\n”,hh,mm,ss);

printf("inputatimeinsecond:");

scanf("%ld",&seconds);

convertime(seconds);

printf("%2d:%2d:%2d\n",hh,mm,ss);

}執(zhí)行時(shí)輸出:

hh=0,mm=0,ss=0

inputatimeinsecond:41574↙(輸入)

11:32:54(輸出)這里的hh、mm、ss是全局于整個(gè)程序的int類型全局變量,其初值為0,它們可以在main()函數(shù)和convertime()函數(shù)中被引用。在main()函數(shù)中,沒有對(duì)它們賦值的語句,第一個(gè)輸出語句輸出的值0是由系統(tǒng)自動(dòng)賦予的初值。在convertime()中分別將轉(zhuǎn)換后的小時(shí)、分、秒賦予hh,mm,ss;從函數(shù)返回后main()函數(shù)中的輸出語句輸出的hh,mm,ss就是在convertime()函數(shù)中被賦予的值。上面的例子表明,全局變量是除參數(shù)和返回值外函數(shù)之間進(jìn)行數(shù)據(jù)通信(傳遞)的又一方式。但對(duì)于一個(gè)可由任何程序調(diào)用的通用函數(shù)而言宜采用參數(shù)進(jìn)行數(shù)據(jù)通信。因?yàn)閰?shù)方式只需遵守?cái)?shù)據(jù)類型和數(shù)目上的規(guī)定,不需要實(shí)參變量與形參變量同名;用全局變量傳遞數(shù)據(jù)則要求調(diào)用函數(shù)和被調(diào)用函數(shù)必須使用相同的變量名,且不能與被調(diào)用函數(shù)的局部變量同名,這樣會(huì)降低函數(shù)的通用性,對(duì)程序的結(jié)構(gòu)化設(shè)計(jì)不利。7.5.6寄存器變量計(jì)算機(jī)的中央處理單元(CPU)中包含多個(gè)寄存器(數(shù)據(jù)存儲(chǔ)單元),諸如加法、除法等實(shí)際的數(shù)據(jù)操作都在其中進(jìn)行。在處理數(shù)據(jù)時(shí),CPU將數(shù)據(jù)從存儲(chǔ)器通過總線移到這些寄存器,處理完畢后,再將數(shù)據(jù)通過總線移回存儲(chǔ)器。CPU的速度可以很快,而總線的速度則要慢得多,所以運(yùn)算時(shí)間主要消耗在數(shù)據(jù)從存儲(chǔ)器讀出或?qū)懭肷?。如果一開始就將一個(gè)特定變量保存在一個(gè)寄存器中,變量的處理就要快得多。C語言允許用戶向編譯程序申請(qǐng),將局部自動(dòng)變量保存在CPU的寄存器中而不是在常規(guī)存儲(chǔ)器中。在一個(gè)自動(dòng)變量的定義中包含register關(guān)鍵字,其作用是請(qǐng)求編譯器將變量保存在一個(gè)寄存器中。寄存器變量除在可能情況下用寄存器分配存儲(chǔ)以及不能取地址之外,其余特性完全與自動(dòng)變量相同。對(duì)于使用頻繁的值,使用寄存器變量可以提高程序運(yùn)行速度。下面的例子可說明寄存器變量的用法。

例7.15

計(jì)算s=x1+x2+x3+…+xn,x和n由終端輸入。

#include<stdio.h>

longsum(registerintx,intn)

{

longs;

inti;

registerintt;

t=s=x;

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

{

t*=x;

s+=t;

}

return(s);}

voidmain()

{

intx,n;

printf("Inputx,n:");

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

printf("s=%ld\n",sum(x,n));

}

執(zhí)行時(shí)輸出:

Inputx,n:45

s=1364其中4和5分別為鍵入的x和n。計(jì)算機(jī)的寄存器是有限的,為確保寄存器用于最需要的地方,應(yīng)將使用最頻繁的值說明為寄存器存儲(chǔ)類型,通常用于使用最頻繁的整型或字符型值。說明為寄存器存儲(chǔ)類型的局部變量首先在寄存器中分配存儲(chǔ),如果無足夠的寄存器,則和自動(dòng)變量一樣在內(nèi)存中分配存儲(chǔ)。目前,大部分編譯系統(tǒng)(如TC、VisualC++等)都不支持真正的register變量,而仍然把register變量保存在存儲(chǔ)器中,因此,一般不必使用register變量。7.6編譯預(yù)處理

C語言提供編譯預(yù)處理的功能,這是它與其它高級(jí)語言的一個(gè)重要區(qū)別。在C編譯系統(tǒng)程序進(jìn)行通常的編譯前,先對(duì)程序中這些特殊的命令進(jìn)行“預(yù)處理”,然后將預(yù)處理的結(jié)果和源程序一起再進(jìn)行通常的編譯處理,以得到目標(biāo)代碼。C語言通過預(yù)處理程序提供了一些語言功能,預(yù)處理程序從理論上講是編譯過程中單獨(dú)進(jìn)行的第一個(gè)步驟。

C語言提供的預(yù)處理功能主要有三種,即宏定義、文件包含和條件編譯,分別用宏定義命令、文件包含命令、條件編譯命令來實(shí)現(xiàn)。為了與一般C語句區(qū)別,這些命令以符號(hào)“#”開頭。7.6.1宏定義宏定義即#define指令,具有如下形式:

#define名字替換文本它是一種最簡單的宏替換——出現(xiàn)各自的名字都將被替換文本替換。宏定義是由源程序中的宏定義命令完成的。宏替換是由預(yù)處理程序自動(dòng)完成的。在#語言中,“宏”分為有參數(shù)和無參數(shù)兩種。下面分別討論這兩種“宏”的定義和調(diào)用。

1.不帶參數(shù)的宏定義用一個(gè)指定的標(biāo)識(shí)符(即名字)來代表一個(gè)字符串,它的一般形式為:

#define標(biāo)識(shí)符字符串其中的“#”表示這是一條預(yù)處理命令。凡是以“#”開頭的均為預(yù)處理命令?!癲efine”為宏定義命令。“標(biāo)識(shí)符”為所定義的宏名。“字符串”可以是常數(shù)、表達(dá)式、格式串等。正常情況下,替換文本是#define指令所在行的剩余部分,但也可以把一個(gè)比較長的宏定義分成若干行,這時(shí)只需在尚待延續(xù)的行后加上一個(gè)反斜杠“\”即可。常對(duì)程序中反復(fù)使用的表達(dá)式進(jìn)行宏定義。例如:

#defineM(y*y+3*y)定義M表達(dá)式(y*y+3*y)。在編寫源程序時(shí),所有的(y*y+3*y)都可由M代替,而對(duì)源程序作編譯時(shí),將先由預(yù)處理程序進(jìn)行宏代換,即用(y*y+3*y)表達(dá)式去置換所有的宏名M,然后再進(jìn)行編譯。例7.16

用宏定義計(jì)算s(y)=y2+3y。

#include<stdio.h>

#defineM(y*y+3*y)

voidmain()

{

ints,y;

printf("inputanumber:");

scanf("%d",&y);

s=3*M+4*M+5*M;

printf("s=%d\n",s);

}上例程序中首先進(jìn)行宏定義,定義M表達(dá)式(y*y+3*y),在s=3*M+4*M+5*M中作了宏替換。在預(yù)處理時(shí)經(jīng)宏展開后該語句變?yōu)椋簊=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y)。

說明:

(1)宏名一般習(xí)慣用大寫字母表示,以與變量名區(qū)別。但這并非規(guī)定,也可用小寫字母。

(2)使用宏名代替一個(gè)字符串,可以減少程序中重復(fù)書寫某些字符串的工作量。

(3)宏定義是用宏名代替一個(gè)字符串,也就是作簡單的置換,不作語法檢查。

(4)宏定義不是C語句,不必在行末加分號(hào),如果加了分號(hào)則會(huì)連分號(hào)一起進(jìn)行置換。

(5)#define命令出現(xiàn)在程序中函數(shù)的外面,宏名的有效范圍為定義命令之后到本源文件結(jié)束。通常,#define命令寫在文件開頭、函數(shù)之前,作為文件的一部分,在文件范圍內(nèi)有效。

(6)可以用#undef命令終止宏定義的作用域。例如:

#definePI3.14159

voidmain()

{

}

#undefPI的作用域

voidf1()

{

}表示PI只在main()函數(shù)中有效,在f1()中無效。

(7)在進(jìn)行宏定義時(shí),可以引用已定義的宏名,并層層替換。

(8)對(duì)程序中用雙引號(hào)括起來的字符,即使與宏名相同,也不進(jìn)行替換。例如:

#include<stdio.h>

#defineOK100

voidmain()

{

printf("OK");

}定義宏名OK表示100,但在printf語句中OK被引號(hào)括起來,因此不作宏代換。程序的運(yùn)行結(jié)果為:OK。這表示把“OK”當(dāng)字符串處理。

2.帶參數(shù)的宏定義帶參數(shù)的宏定義不僅是進(jìn)行簡單的字符串替換,還要進(jìn)行參數(shù)替換。其定義的一般形式為:

#define宏名(參數(shù)表)字符串字符串中包含在括弧中所指的參數(shù)。如:

#defineS(a,b)a*b

area=S(3,2);對(duì)帶參的宏定義是這樣展開置換的:在程序中如果有帶實(shí)參的宏(如S(3,2)),則#define命令行中的字符串從左到右進(jìn)行置換。如果串中包含宏中的形參(如(a,b)),則將程序語句中相應(yīng)的實(shí)參(可以是常量、變量或表達(dá)式)代替形參,如果宏定義中的字符串中的字符不是參數(shù)字符(如a*b中的*號(hào)),則保留。這樣就形成了字符串,見圖7.4。圖7.4帶參數(shù)的宏定義的置換說明:

(1)對(duì)帶參的宏展開只是將語句中的宏名后面的括號(hào)內(nèi)的實(shí)參字符串代替#define命令行中的形參。

(2)在宏定義時(shí),在宏名與帶參的括號(hào)之間不應(yīng)加空格,否則將空格以后的字符串都作為代替字符串的一部分。

(3)定義宏時(shí),最好將參數(shù)和宏體用括號(hào)括起來。如:#definesquare(n)n*n,調(diào)用時(shí)s=square(a+1),則變成了s=a+1*a+1,與預(yù)期效果不同。

7.6.2文件包含處理所謂的文件包含處理,是指一個(gè)源文件可以將另外一個(gè)源文件的全部內(nèi)容包含進(jìn)來,即將另外的文件包含到本文件之中。C語言提供了#include命令來實(shí)現(xiàn)文件包含的操作。其一般形式為:

#include"文件名"例如:

#include"stdio.h"

#include"math.h"圖7.5表示文件包含的含意。圖7.5(a)為文件file1.c,它有一個(gè)#include<file2.c>命令,而且還有其它內(nèi)容(以A表示)。圖7.5(b)為另一文件file2.c,文件內(nèi)容以B表示。在編譯處理時(shí),要對(duì)#include命令進(jìn)行文件包含處理:將file2.c的全部內(nèi)容復(fù)制插入到include<file2.c>命令處,即file2.c被復(fù)制到file1.c中,得到圖7.5(c)所示的結(jié)果。在編譯中,將“包含”以后的file1.c(即圖7.5(c)所示)作為一個(gè)源文件單獨(dú)進(jìn)行編譯。圖7.5文件包含用在文件頭部的被包含的文件稱為標(biāo)題文件或頭部文件,常以“.h”為后綴。如果需要修改一些常數(shù),不必修改每個(gè)程序,只需修改一個(gè)文件(頭部文件)即可。頭部文件除了可以包括宏定義外,也可以包括結(jié)構(gòu)體類型定義、全局變量定義等。

說明:

(1)一個(gè)#include命令只能指定一個(gè)被包含

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(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)論