版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第七章函數(shù)及變量存儲類型7.1函數(shù)基礎(chǔ)與C程序結(jié)構(gòu)
7.2函數(shù)的定義和聲明7.3函數(shù)的調(diào)用7.4函數(shù)的嵌套與遞歸7.5變量的存儲類別7.6編譯預(yù)處理7.7程序設(shè)計舉例習(xí)題7.1函數(shù)基礎(chǔ)與C程序結(jié)構(gòu)7.1.1C程序的結(jié)構(gòu)化設(shè)計思想當(dāng)設(shè)計一個解決復(fù)雜問題的程序時,傳統(tǒng)的面向過程的程序設(shè)計方法為了能清楚地描述程序的運行過程,要求將一個復(fù)雜的任務(wù)劃分為若干子任務(wù),每個子任務(wù)設(shè)計成一個子程序,稱為模塊。若子任務(wù)較復(fù)雜,還可以將子任務(wù)繼續(xù)分解,直到分解成為一些容易解決的子任務(wù)為止。每個子任務(wù)對應(yīng)于一個子程序,子程序在程序編制(即代碼)上相互獨立,而在對數(shù)據(jù)的處理上又相互聯(lián)系,數(shù)據(jù)和程序代碼是分開存儲的。完成總?cè)蝿?wù)的程序由一個主程序和若干子程序組成,主程序起著任務(wù)調(diào)度的總控作用,而每個子程序各自完成一個單一的任務(wù)。這種自上而下逐步細化的模塊化程序設(shè)計方法就是所謂結(jié)構(gòu)化程序設(shè)計方法。結(jié)構(gòu)化程序設(shè)計的優(yōu)點是:程序編制方便,易于修改和調(diào)試,可由多人分工合作完成;程序結(jié)構(gòu)模塊化,可讀性、可維護性及可擴充性強;子程序代碼公用(當(dāng)需要完成同樣任務(wù)時,只需要一段代碼,可多次調(diào)用),使程序簡潔。
C語言是函數(shù)式語言,沒有子程序,程序員可利用函數(shù)來實施結(jié)構(gòu)化程序設(shè)計,即單一的程序任務(wù)由獨立的函數(shù)來完成,不要試圖在一個函數(shù)中完成所有的任務(wù),一個函數(shù)只應(yīng)完成一件單一的任務(wù)。一個成功的C程序應(yīng)該是由多個功能簡單、獨立的函數(shù)構(gòu)成的。要善于利用函數(shù),以減少重復(fù)編程。組成一個C程序的各函數(shù)可以分開編輯成多個C源文件。一個C源文件中可以含有0個(源文件中可以沒有函數(shù),僅由一些說明組成,例如定義一些全局變量)或多個函數(shù),因而一個C程序可以有一個或多個源文件,每個源文件是一個編譯單位。源文件被編譯之后生成二進制代碼形式的目標程序文件,組成一個C程序的所有源文件都被編譯之后,由連接程序?qū)⒏髂繕宋募械哪繕撕瘮?shù)和系統(tǒng)標準函數(shù)庫的函數(shù)裝配成一個可執(zhí)行的C程序。
C程序結(jié)構(gòu)示意圖如圖7.1所示。圖7.1C程序結(jié)構(gòu)示意圖7.1.2函數(shù)概述在C程序設(shè)計中,函數(shù)是獨立的C程序模塊,它完成一個特定任務(wù)并可選擇是否將一個值返回調(diào)用程序。在C語言中,子程序被稱為函數(shù),它相應(yīng)于其它高級語言中的過程(無返回值的子程序)和函數(shù)(通過函數(shù)名返回一個值的子程序)。一個C程序一般由多個函數(shù)組成,其中必須有一個且僅有一個名為main的主函數(shù),其余為被main()函數(shù)或其它函數(shù)調(diào)用的函數(shù),無論main()函數(shù)位于程序中的什么位置,C程序總是從main()函數(shù)開始執(zhí)行。
main()函數(shù)可調(diào)用其它函數(shù)來實現(xiàn)所需的功能。被main()函數(shù)調(diào)用的函數(shù)分為兩類。一類是由系統(tǒng)提供的標準庫函數(shù),例如,標準輸入/輸出函數(shù)(scanf,printf,getche,putchar,…)、數(shù)學(xué)計算函數(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;
}運行結(jié)果:
Inputaninteger:100↙
(輸入)
Thesquarenumberof100is10000(輸出)分析:
(1)“l(fā)ongsquare(long);”語句是一個函數(shù)聲明,函數(shù)定義將在程序的后面出現(xiàn)。函數(shù)聲明指出了函數(shù)原型,包括函數(shù)名、要傳送過來的參數(shù)表以及它所返回的函數(shù)值的類型。程序員和編譯系統(tǒng)從這個語句可了解到:函數(shù)名為square,函數(shù)需要一個long型參數(shù),并將返回一個long型值。
(2)語句“result=square(in_num);”調(diào)用square()函數(shù)并將變量in_num作為實參傳送給它。該函數(shù)的返回值賦予變量result。注意in_num和result為long型變量,以便與函數(shù)原型相匹配。
(3)longsquare(longx)開始函數(shù)定義,函數(shù)定義的首部(即第一行)給出函數(shù)的返回類型、函數(shù)名和形參描述。圍在大括號中的是函數(shù)體,其中包括函數(shù)內(nèi)部的一些說明和變量定義以及函數(shù)在被調(diào)用時要執(zhí)行的語句。
(4)函數(shù)以一個return語句終結(jié),return語句將一個值傳回調(diào)用程序并結(jié)束函數(shù)的調(diào)用。本例中,返回變量x_square的值。比較square()函數(shù)與main()函數(shù)的結(jié)構(gòu)可知它們是類似的,注意main()雖是一個函數(shù),但不可被調(diào)用。本書已用過的printf()和scanf()函數(shù)是庫函數(shù),與用戶定義函數(shù)不同,但用戶在調(diào)用時的操作是相似的。由此可總結(jié),C程序中的函數(shù)具有以下特性:
(1)每個函數(shù)都有惟一的名字(如square),用這個名字,程序可以轉(zhuǎn)去執(zhí)行該函數(shù)所包括的語句,這種操作稱為調(diào)用函數(shù)。一個函數(shù)能被另一個函數(shù)調(diào)用,如在main()函數(shù)中調(diào)用square()函數(shù),但main()函數(shù)不可被其它任何函數(shù)調(diào)用。
(2)一個函數(shù)執(zhí)行一個特定的任務(wù),此任務(wù)是程序必須完成的全部操作中一個獨立的操作行為,如將一行正文傳送到打印機,將一個數(shù)組按數(shù)值順序排列,求一個立方根等等。
(3)函數(shù)的定義是獨立的、封閉的。一個函數(shù)的定義應(yīng)該不受程序其它部分的干預(yù),也應(yīng)不干預(yù)其它部分而完成自身的任務(wù)。
(4)函數(shù)能給調(diào)用程序返回一個值。在程序調(diào)用一個函數(shù)時,此函數(shù)所包含的語句便會執(zhí)行,必要時,還能將信息傳回調(diào)用程序。
C程序中的函數(shù),只有在被調(diào)用時,其中的語句才能執(zhí)行。程序在調(diào)用一個函數(shù)時,可用一個或多個實參將信息傳送到函數(shù),實參往往是函數(shù)在執(zhí)行任務(wù)時所需的數(shù)據(jù)。函數(shù)中的語句執(zhí)行后,就完成了指定的任務(wù)。當(dāng)函數(shù)的語句全部完成后,程序返回到該函數(shù)被調(diào)用處,函數(shù)也能以返回值的形式將信息傳送回程序。7.2函數(shù)的定義和聲明7.2.1函數(shù)的定義函數(shù)的定義就是對函數(shù)所要完成功能的操作進行描述的過程。它一般包括函數(shù)名的命名和類型說明、形式參數(shù)的類型說明、必要的變量定義、操作語句等等。下面首先看一個函數(shù)定義的實例,然后給出函數(shù)定義的一般形式。
例7.2
計算x的n次方,x=2,-3;n=1,2,…,9。分析:根據(jù)題意,x有兩個取值,每個x值對應(yīng)于9個n值,因此程序要計算18次x的冪值。所以最好將計算x的冪定義成函數(shù),函數(shù)名為power,參數(shù)為x和n。main()函數(shù)每次用不同的x和n值調(diào)用power()函數(shù),并輸出計算結(jié)果。程序如下:
#include<stdio.h>
intmain(void)/*測試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是一個標識符,power()函數(shù)具有double類型的返回值,它有兩個int類型的參數(shù)x和n。{}括起來的部分是函數(shù)體,其中的說明部分“inti;doublep;”說明i、p是在power()函數(shù)內(nèi)部使用的局部變量。執(zhí)行部分的“return(p);”語句將表達式p的值返回給main()函數(shù)的調(diào)用處,p的值就是power()函數(shù)的返回值(簡稱函數(shù)值)。函數(shù)定義的一般形式為:存儲類型標識符類型標識符函數(shù)名(形式參數(shù)表列及類型說明)
{說明部分語句部分}函數(shù)定義由函數(shù)首部和函數(shù)體兩部分組成。函數(shù)首部即定義一個函數(shù)時的第一行,包括存儲類型標識符、類型標識符、函數(shù)名和由()括起來的參數(shù)表;{}部分稱為函數(shù)體,語法上是一個復(fù)合語句。各部分說明如下:
1)存儲類型標識符存儲類型標識符說明函數(shù)的存儲類型,它規(guī)定了函數(shù)可被調(diào)用的范圍??捎糜诤瘮?shù)的存儲類型標識符有static和extern,指定為static的函數(shù)為靜態(tài)函數(shù),靜態(tài)函數(shù)只能由和它在同一文件中定義的函數(shù)調(diào)用;不指定存儲類型標識符時為缺省的存儲類型extern,缺省或指定為extern存儲類型的函數(shù)是外部函數(shù),例如,例7.2中的power()函數(shù)是外部函數(shù)。
2)類型標識符
C程序中定義的函數(shù)可以什么也不返回而只完成某項工作。無返回值的函數(shù),類型標識符為void,又稱為“空類型函數(shù)”,即此函數(shù)不向主調(diào)函數(shù)返回值,主調(diào)函數(shù)也禁止使用此函數(shù)的返回值。
C程序中定義的函數(shù)也可以返回一個值,這時,類型標識符說明函數(shù)返回值的數(shù)據(jù)類型(常簡稱為“函數(shù)值的類型”或“函數(shù)的類型”),例如,例7.2中的power()函數(shù)是一個double類型的函數(shù),main()是int類型的函數(shù)。函數(shù)的類型可以為任何基本類型、結(jié)構(gòu)體類型。還可以定義返回值為指針的函數(shù),但不能定義返回數(shù)組的函數(shù)。int型函數(shù)定義時可以省略類型標識符int,因為int是有返回值函數(shù)的缺省類型(提倡明確指出int)。
3)函數(shù)名函數(shù)名是一個標識符,一個程序中除主函數(shù)main()外,其余函數(shù)的名字可以任意取,最好取有助于記憶的名字。考慮到與外部聯(lián)接的需要,函數(shù)名一般不要超過6個字符長,如max()、power()和factor()等。外部函數(shù)的名字要作用于整個程序,因而外部函數(shù)相互之間不能同名。靜態(tài)函數(shù)可以和外部函數(shù)同名,但同一文件中的函數(shù)不能同名。
4)參數(shù)表函數(shù)定義中的參數(shù)表說明函數(shù)參數(shù)的名稱、類型和數(shù)目。參數(shù)表由零個或多個參數(shù)說明組成,如果函數(shù)沒有參數(shù),可只寫一對括號(此為函數(shù)標志,不可省略),但最好將參數(shù)表指定為void。有多個參數(shù)時,多個參數(shù)之間用逗號隔開。函數(shù)定義中的參數(shù)表習(xí)慣上稱為形參表。形參說明的一般形式為:
類型標識符形參名每個類型標識符對應(yīng)于一個形參名,當(dāng)有多個形參時,相互間用逗號隔開。例如,例7.2中的main()函數(shù)沒有參數(shù),形參表為void;power()函數(shù)有兩個形參,其形參表示為:intx,intn形參是局部變量,僅在本函數(shù)內(nèi)有定義,任何其它函數(shù)不能使用它們進行存取。
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,但它們各自有自己的存儲單元,是完全不同的兩個變量。函數(shù)體語法上是一個復(fù)合語句,它可以沒有說明部分而只有執(zhí)行部分,也可以兩者都沒有。因此,最簡單的合法函數(shù)是形參表為空(void)且函數(shù)體也為空的函數(shù)(稱為啞函數(shù))。例如:
voiddummy(void){}dummy()函數(shù)被調(diào)用時,它不執(zhí)行任何操作,僅在調(diào)用程序的流程控制中占有一個位置。當(dāng)調(diào)用程序的功能需要擴充時,可編寫一個具有新功能的函數(shù),并用對新函數(shù)的調(diào)用取代相應(yīng)的啞函數(shù)調(diào)用。這在程序開發(fā)的初級階段是非常有用的,讀者可參考配套的《〈C程序設(shè)計〉學(xué)習(xí)指導(dǎo)(第二版)》書中的應(yīng)用系統(tǒng)設(shè)計題來體會。
void類型函數(shù)不含return或含不帶表達式的return語句。有返回值的函數(shù)必須至少包含一個帶表達式的return語句,表示函數(shù)調(diào)用至此結(jié)束,返回到主調(diào)函數(shù)的函數(shù)調(diào)用處。
return表達式;或
return(表達式);表達式的值就是函數(shù)的返回值。對于基本類型,表達式的類型和函數(shù)的類型不相同時表達式的值自動轉(zhuǎn)換為函數(shù)的類型;對于指針,表達式的類型和函數(shù)的類型不相同時,須使用類型強制符將表達式的值轉(zhuǎn)換為函數(shù)的類型;對于結(jié)構(gòu)體,表達式值的類型與函數(shù)定義的類型必須相同。例如,可以將power()函數(shù)定義為:
doublepower(intx,intn)
{
inti;longp;
return(p);
}其中,“return(p)”將表達式p的值作為power()函數(shù)的返回值。p被自動轉(zhuǎn)換成double類型。7.2.2函數(shù)的聲明(函數(shù)原型)
C語言允許函數(shù)先調(diào)用后定義,或被調(diào)用函數(shù)在其它文件中定義。對于此種情況之一的非int型函數(shù),必須在調(diào)用函數(shù)之前作函數(shù)聲明,其目的是指出被調(diào)用函數(shù)的類型和參數(shù)的類型,否則編譯程序認為被調(diào)用函數(shù)為int類型。但是我們需要注意最新的C++標準已不再支持默認的int()函數(shù)和變量,因此,建議大家在定義變量和函數(shù)時,不要省略類型標識符。函數(shù)聲明的一般形式為:存儲類型標識符類型標識符函數(shù)名(形參表);外部函數(shù)聲明時可指定extern或存儲類型標識符缺省,靜態(tài)函數(shù)聲明時必須指定static;參數(shù)表可以只列出參數(shù)的類型名而不需給出參數(shù)名。例如:
doublepower(int,int);和doublepower(intx,intn);和doublepower(inta,intm);都是等價的。power()函數(shù)是double類型,它有兩個int參數(shù)。聲明時給出的參數(shù)名x、n被編譯忽略,因為參數(shù)的存儲分配是在函數(shù)被調(diào)用時進行的。對于無參數(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ù)體中聲明時,還可以寫成“doublepower();”這種形式,在早期的C語言書籍中多采用該形式;而目前流行的形式是采用函數(shù)體外部聲明。例如,在例7.2中main()函數(shù)調(diào)用了power()函數(shù),power()函數(shù)的定義在main()函數(shù)的定義之后,且類型為非int類型,所以在main()的說明部分要對power()函數(shù)作聲明:
doublepower(int,int);也可以在main()函數(shù)外面聲明:
doublepower(int,int);
intmain(void)
{
inti;
}帶參數(shù)表的函數(shù)聲明稱為函數(shù)原型。標準庫函數(shù)的原型在系統(tǒng)提供的相應(yīng)頭文件中,因此,程序中調(diào)用標準庫函數(shù)時,只需用#include預(yù)處理控制包含所需的頭文件,而不需寫函數(shù)聲明。實際上,不管是否必須,對所有被調(diào)用函數(shù)均進行聲明是較好的編程習(xí)慣,既符合現(xiàn)代程序設(shè)計風(fēng)格,又方便了程序的檢查和閱讀。7.3函數(shù)的調(diào)用一個函數(shù)可以被其它函數(shù)多次調(diào)用,每次調(diào)用時可以處理不同的數(shù)據(jù),因此函數(shù)是對不同數(shù)據(jù)進行相同處理的一種通用的程序形式。通常將函數(shù)定義時在參數(shù)表列出的參數(shù)稱為形式參數(shù),簡稱形參。形參是函數(shù)要處理的數(shù)據(jù)名稱(形式上的變量),在函數(shù)定義時并未開辟相應(yīng)的存儲單元,只有到函數(shù)調(diào)用時,系統(tǒng)才為形式參數(shù)分配與其類型長度相同的存儲單元,并將實際要處理的參數(shù)送到形參對應(yīng)的存儲單元。每次調(diào)用時,使用不同的實際數(shù)據(jù)從而實現(xiàn)對不同數(shù)據(jù)的相同處理。調(diào)用時被送到形參單元的實際數(shù)據(jù)通常稱為實際參數(shù),簡稱實參。形參是變量,實參是形參在每次調(diào)用時得到的值。7.3.1函數(shù)調(diào)用的方式和條件函數(shù)調(diào)用的一般形式為:函數(shù)名(實參1,實參2,…,實參n)
()部分稱為實參表列,實參可以是常量、變量或表達式,有多個實參時,相互間用逗號隔開。實參和形參應(yīng)在數(shù)目、次序和類型上一致。對于無參數(shù)的函數(shù),調(diào)用時實參表為空,但()不能省。函數(shù)調(diào)用在程序中起一個表達式或一個語句的作用。對于有返回值的函數(shù),函數(shù)調(diào)用一般作為表達式出現(xiàn),即凡程序中允許出現(xiàn)表達式的位置上均可出現(xiàn)函數(shù)調(diào)用;也可作為語句(即表達式語句)出現(xiàn)。對于無返回值函數(shù)的調(diào)用,只能以語句形式出現(xiàn)。例如:
(1)getch();
getch()函數(shù)調(diào)用作為語句出現(xiàn)。
(2)c=getchar();
getchar()函數(shù)調(diào)用作為表達式出現(xiàn)(賦值表達式的右操作數(shù))。
(3)while(putchar(getche())!=′?′);
getche()函數(shù)調(diào)用作為putchar()函數(shù)調(diào)用的實參(表達式)出現(xiàn),putchar()函數(shù)調(diào)用作為關(guān)系表達式的左操作數(shù)(表達式)出現(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)首先計算實參表達式的值并為每個形參分配存儲單元,然后把實參值復(fù)制到(送到或存入)對應(yīng)形參的存儲單元中。實參與形參按位置一一對應(yīng)。
(2)將控制轉(zhuǎn)移到被調(diào)用的函數(shù),執(zhí)行其函數(shù)體內(nèi)的語句。
(3)當(dāng)執(zhí)行return語句或到達函數(shù)體末尾時,控制返回到調(diào)用處,如果有返回值,同時回送一個值。然后從函數(shù)調(diào)用點繼續(xù)執(zhí)行主調(diào)函數(shù)后面的操作。除了正確地編寫函數(shù)的定義及調(diào)用語句,要想成功地調(diào)用某個函數(shù)還必須滿足下列三個條件之一:
(1)被調(diào)用函數(shù)的定義出現(xiàn)在主調(diào)函數(shù)的定義之前。
(2)在主調(diào)函數(shù)中或主調(diào)函數(shù)之前的外部對被調(diào)用函數(shù)進行聲明。
(3)被調(diào)用函數(shù)為標準函數(shù)時,在函數(shù)調(diào)用前已包含了相應(yīng)的頭文件。7.3.2形參與實參的數(shù)值傳遞函數(shù)調(diào)用時將實參傳送給形參稱為參數(shù)傳遞。C語言中,參數(shù)的傳遞方式是“單向值傳遞”,形參和實參變量各自有不同的存儲單元,被調(diào)用函數(shù)中形參變量值的變化不會影響實參變量的值。
例7.3
形參與實參的數(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);
}運行結(jié)果:
a=10b=20可以看到,在調(diào)用swap()函數(shù)時,實參a和b的值是10和20。進入被調(diào)函數(shù)時,先開辟形參單元x和y,再將a和b的值分別傳遞給形參變量x和y(如圖7.2(a)所示),執(zhí)行swap()函數(shù)使x和y的值進行了交換,但交換的結(jié)果并不會使實參變量a和b交換,所以a和b的值仍然為10和20(如圖7.2(b)所示)。圖7.2swap()函數(shù)的傳遞因此,在執(zhí)行一個被調(diào)用函數(shù)時,形參的值如果發(fā)生改變,并不會改變主調(diào)函數(shù)實參的值。實際上當(dāng)實參為常量或表達式時,這一點更容易理解。在參數(shù)傳遞時有三個問題需要注意:
(1)實參與形參的一致性,實參的數(shù)目和類型應(yīng)該與形參保持一致。如果參數(shù)的數(shù)目不一致或類型不一致,則調(diào)用的效果不確定,這是程序得不到正確結(jié)果的原因之一。
(2)C語言中可以定義參數(shù)數(shù)目可變的函數(shù)。定義這種函數(shù)時,要求至少要給出一個形參,在列出的最后一個形參后面用逗號后再跟三個點,即“,…”來聲明該函數(shù)的參數(shù)數(shù)目可變。調(diào)用參數(shù)數(shù)目可變的函數(shù)時,實參的數(shù)目不能少于(可以多于)形參表中列出的形參的數(shù)目,即最后一個逗號之前的形參的數(shù)目。實參在類型和次序上同樣應(yīng)與形參一致。printf()和scanf()函數(shù)就是C中最常用的參數(shù)數(shù)目可變的函數(shù)。例如,printf()函數(shù)調(diào)用的一般形式為:
printf(格式字符串,參數(shù)1,參數(shù)2,…);其中第一個參數(shù)(格式字符串)是必需的,調(diào)用時系統(tǒng)根據(jù)第一個參數(shù)中的格式說明項的數(shù)目和格式說明字符來確定其余參數(shù)的數(shù)目和類型,例如:
printf("x=%dy=%f",x,y);因為第一個參數(shù)中有兩個格式說明項,分別為%d和%f,所以確定printf()在本次調(diào)用中還有兩個輸出參數(shù),分別為整數(shù)和浮點數(shù)。
(3)函數(shù)調(diào)用時,每一實參為一表達式,實參與實參間的逗號是分隔符,不是順序求值運算符,它不保證參數(shù)的求值順序按從左至右進行,參數(shù)的求值順序由具體系統(tǒng)確定。多數(shù)編譯程序在計算參數(shù)值時按從右至左的順序進行。例如在TurboC中運行下列程序。
例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í)行時輸出:
x=0
x++=1x++=0
x=2在該程序的第二個printf語句中,右邊的x++先求值,左邊的x++后求值,因此右邊的x++的輸出為0,左邊的為1。7.3.3函數(shù)的返回值當(dāng)函數(shù)為有返回值的函數(shù)時,在函數(shù)中要用return后跟一個C表達式構(gòu)成的語句完成。在執(zhí)行到返回語句時,表達式求值,并將其值返回到調(diào)用程序,函數(shù)的返回值就是該表達式的值。例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;
}運行結(jié)果:
2.55.6
max=5.00在max()函數(shù)被調(diào)用時,函數(shù)體中的語句執(zhí)行到return語句,return終止函數(shù)的執(zhí)行并將y的值返回給調(diào)用程序,return關(guān)鍵字之后的表達式可以是任意的C表達式。如例7.5所示,函數(shù)中可包含多個return語句??梢钥吹胶瘮?shù)的類型與return語句中的表達式的值不一致,此時以函數(shù)類型為準。對數(shù)值型數(shù)據(jù),可以自動進行類型轉(zhuǎn)換,即函數(shù)類型決定返回值的類型。一般情況下提倡函數(shù)類型與return語句中的表達式的值一致。若函數(shù)不需要返回值時,可以用void定義函數(shù)類型,此時函數(shù)中不可有帶表達式的return語句。如果函數(shù)的類型不是void,即使函數(shù)中沒有return語句,函數(shù)也有返回值,只是返回值為不確定的值。需要說明的是,現(xiàn)代的C++語法要求越來越嚴格,新的編譯系統(tǒng)甚至已經(jīng)不支持返回值為int型的函數(shù)省略“int”返回類型的寫法,即認為max(intx,inty)這種形式是非法,必須寫成intmax(intx,inty)。因此,我們在編寫函數(shù)時,必須寫出函數(shù)的返回值類型,當(dāng)有返回值時,一定要有return語句。在C語言中,main()函數(shù)是一個特殊的函數(shù),它由操作系統(tǒng)調(diào)用,然后返回到操作系統(tǒng)。因此,一般來講,main()函數(shù)是有返回值的,其返回值為int類型。我們在編寫程序時,如果main()函數(shù)的形式是intmain(){…},函數(shù)的結(jié)尾必須有“return1;”之類的語句;而經(jīng)常地,我們也把main()函數(shù)寫成voidmain(){…}這種形式,表示沒有給操作系統(tǒng)返回任何值,無需寫“return1;”這樣的語句,從而顯得更簡潔。在本書中,我們多用voidmain()這樣的形式。7.4函數(shù)的嵌套與遞歸當(dāng)一個函數(shù)作為被調(diào)用函數(shù)時,它也可以作為另一個函數(shù)的主調(diào)函數(shù),而它的被調(diào)用函數(shù)又可以調(diào)用其它函數(shù),這就是函數(shù)的嵌套調(diào)用。當(dāng)一個函數(shù)直接或間接地調(diào)用它自身時,稱為函數(shù)的遞歸。C語言規(guī)定任何函數(shù)都可以調(diào)用其它函數(shù),且除了main()函數(shù)以外的任何函數(shù)都可以作為其它函數(shù)的被調(diào)用函數(shù)。7.4.1函數(shù)的嵌套調(diào)用函數(shù)的定義是不允許嵌套的,但調(diào)用是可以的。實際上,在稍微復(fù)雜一點的程序中,嵌套調(diào)用是常常發(fā)生的。
例7.6
用牛頓迭代法求根。方程為ax3+bx2+cx+d=0,系數(shù)由主函數(shù)輸入。求x在1附近的一個根。求出根后由主函數(shù)輸出。
分析:牛頓迭代法的公式是x=x0-(f(x)/f′(x)),假設(shè)迭代到|x-x0|≤10-5時結(jié)束。求解時,先計算函數(shù)f(x0),再計算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));
}
運行結(jié)果:
Inputa,b,c,d,eps
12341e-6↙
x=-1.6506292可以看到在程序運行時,函數(shù)main()調(diào)用了函數(shù)Newton(),而函數(shù)Newton()又調(diào)用了函數(shù)f()和df(),這樣就形成了函數(shù)的嵌套調(diào)用。7.4.2函數(shù)的遞歸及條件遞歸是一種特殊的解決問題的方法,要用遞歸解決問題,應(yīng)滿足兩個條件:
(1)函數(shù)直接或間接地調(diào)用它本身;
(2)應(yīng)有使遞歸結(jié)束的條件。例7.7
用遞歸方法求n!。
分析:求n!可以用遞推方法,即從1開始,乘2,再乘3……一直乘到n;也可以用遞歸方法實現(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);
}
運行結(jié)果:
inputaintegernumber:10↙
10!=362800例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ù)可否進行遞歸調(diào)用?7.5變量的存儲類別
C語言的數(shù)據(jù)有兩種屬性:數(shù)據(jù)類型和存儲類型。因此完整的變量說明的一般形式為:存儲類型標識符類型標識符變量名;其中類型標識符說明變量的數(shù)據(jù)類型,在前面章節(jié)已經(jīng)講述了數(shù)據(jù)類型,如整型、實型等。本節(jié)講述數(shù)據(jù)的存儲類型,C語言的數(shù)據(jù)有四種存儲類型,分別由四個關(guān)鍵字(稱為存儲類型標識符)表示:auto(自動)、extern(全局)、static(靜態(tài))和register(寄存器)。7.5.1變量的作用域和生存期變量的作用域是指一個范圍,這個范圍內(nèi)程序的各個部分都可訪問該變量。換句話說,變量在這個范圍內(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的值??煽吹絰并未作為一個實參傳送到函數(shù)print_value(),而是直接作為printf()中的一個實參。這是因為變量x的作用域包括了main()函數(shù)和print_value()函數(shù)?,F(xià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);
}上述程序在編譯時將會提示在第11行有錯誤——未定義變量x。這是因為變量x的定義位于main()函數(shù)內(nèi),它的作用域也只限于main()內(nèi),在print_value()函數(shù)內(nèi),變量x未被定義或者說是不可見的。上面兩個例子的惟一區(qū)別在于變量x定義的位置,將x的定義移位,其作用域發(fā)生了變化。在例7.9中,x定義在函數(shù)的外部并位于文件的最前面,其作用域為整個程序,在main()函數(shù)與printf_value()函數(shù)內(nèi)都是可訪問的。在例7.10中,x定義在main()函數(shù)的內(nèi)部,其作用域局限于main()函數(shù)中。為了理解變量作用域的重要性,可復(fù)習(xí)一下在本章前面所講的結(jié)構(gòu)化程序設(shè)計思想。結(jié)構(gòu)化方法將程序劃分為分別執(zhí)行特定任務(wù)的獨立函數(shù),為了具備真正的獨立性,每個函數(shù)中的變量必須隔離以避免來自其它函數(shù)的干擾。函數(shù)之間的完全數(shù)據(jù)隔離并不總是必要的。但是,通過指定變量的作用域,程序員可更進一步地控制數(shù)據(jù)隔離的程度。當(dāng)使用一個變量時,需要首先在內(nèi)存中給這個變量開辟相應(yīng)的存儲單元,這時可以說這個變量存在了,或說它處于生存期內(nèi)。如果這個變量所占用的內(nèi)存單元被釋放,那么這個變量就不存在了,或說在生存期之外。所以生存期指變量在內(nèi)存中占用內(nèi)存單元的時間。當(dāng)一個程序運行時,程序中所包含的變量并不一定在程序運行的整個過程中都占用內(nèi)存單元,往往是在需要時占用內(nèi)存,而在使用結(jié)束后釋放內(nèi)存,這樣做可以提高內(nèi)存單元的使用效率,所以就產(chǎn)生了變量的生存期問題。7.5.2動態(tài)存儲和靜態(tài)存儲內(nèi)存中供用戶使用的存儲空間可分為程序區(qū)、動態(tài)存儲區(qū)和靜態(tài)存儲區(qū),如圖7.3所示。程序區(qū)用來存放程序代碼,動態(tài)存儲區(qū)和靜態(tài)存儲區(qū)用來存放數(shù)據(jù),即數(shù)據(jù)與處理數(shù)據(jù)的程序是分離的,這是面向過程的程序設(shè)計方法的特點。靜態(tài)存儲區(qū)即全局數(shù)據(jù)區(qū),用來存放程序的全局數(shù)據(jù)和靜態(tài)數(shù)據(jù)。圖7.3程序的內(nèi)存使用動態(tài)存儲區(qū)又分為堆區(qū)和棧區(qū)。堆區(qū)用來存放程序的動態(tài)數(shù)據(jù);棧區(qū)用來存放程序的局部數(shù)據(jù),即各個函數(shù)中的數(shù)據(jù)。動態(tài)存儲和靜態(tài)存儲是指C對數(shù)據(jù)存儲的兩種方式。動態(tài)存儲是指存儲一些數(shù)據(jù)的存儲單元可在程序運行的不同時間分配給不同的數(shù)據(jù),而靜態(tài)存儲是指存儲單元在程序運行的整個過程中固定地分配給某些數(shù)據(jù)。動態(tài)存儲區(qū)中數(shù)據(jù)的生存期一般是程序運行中的某個階段,而靜態(tài)存儲區(qū)中數(shù)據(jù)的生存期為整個程序運行過程。決定數(shù)據(jù)存放在內(nèi)存的哪個區(qū)域,是由變量定義時存儲類型標識符和變量定義的位置所決定的。7.5.3局部變量局部變量又稱內(nèi)部變量,是在一個函數(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ù)被認為是局部變量,在函數(shù)被調(diào)用時才會在內(nèi)存的動態(tài)存儲區(qū)中開辟存儲單元,函數(shù)調(diào)用結(jié)束時與此函數(shù)內(nèi)的其它局部自動變量一樣釋放所占有的內(nèi)存單元。局部變量的存儲類型可以通過類型標識符auto和static來規(guī)定。利用auto定義的變量存放在動態(tài)存儲區(qū)中,auto可以缺??;利用static定義的變量存放在靜態(tài)存儲區(qū)中。編譯器并不將局部自動變量預(yù)置為0。如果一個局部變量未在定義時初始化,其值是不確定的或無意義的。程序員必須在局部變量開始使用之前確切地給它們賦值。不同函數(shù)中可以使用相同名字的局部變量,它們代表不同的變量,相互不會形成干擾。局部變量還可以與全局變量同名,此時在局部變量的作用域內(nèi),全局變量不起作用。例7.11
局部變量與全局變量同名。
#include<stdio.h>
inta=1,b=2;/*定義全局變量a、b*/
intmax(inta,intb)/*子函數(shù)中的局部變量a、b*/
{
intc;/*等價于autointc;*/
c=a>b?a:b;
return(c);
}
voidmain()
{
inta=8;/*定義局部變量a*/
printf("%d",max(a,b));
}運行結(jié)果:
8程序中有兩個全局變量a、b,在max()函數(shù)中有形參a、b,形參相當(dāng)于局部變量,在函數(shù)調(diào)用時它們的值來自于實參的值,與同名全局變量無關(guān)。而在main()函數(shù)中有局部變量a,在函數(shù)中使用的變量a為局部變量,值為8,與同名全局變量無關(guān),使用的變量b為全局變量,值為2。所以max()函數(shù)形參得到的值分別為8和2,最后返回值為8。例7.11中的局部變量定義時都缺省了存儲類型的說明,這就相當(dāng)于用auto標識符進行定義,也就是說定義為局部自動變量,在動態(tài)存儲區(qū)中存放。局部變量可以用static標識符定義為局部靜態(tài)變量,在靜態(tài)存儲區(qū)中存放。局部靜態(tài)變量的作用域仍然是定義它的函數(shù)內(nèi),但因為它是靜態(tài)存儲類型,所以它的生存期為程序運行的整個過程。局部靜態(tài)變量與局部動態(tài)變量在使用上也有較大區(qū)別。7.5.4局部靜態(tài)變量的使用局部靜態(tài)變量具有一定的特殊性,它在程序運行的整個過程中都占用內(nèi)存單元,當(dāng)其所在的函數(shù)被調(diào)用時,它才可以被使用,而在函數(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();
}運行結(jié)果:
4466
4467在函數(shù)f()中,a和b為局部自動變量,在每次調(diào)用f()時,a和b都會重新開辟內(nèi)存單元。對于變量b來說,也會重新賦初值,這樣兩次調(diào)用得到的結(jié)果是一樣的。而變量c和d為局部靜態(tài)變量,它們在程序運行開始時就獲得了內(nèi)存中的存儲單元,且變量d進行了賦初值,而c未賦初值。在調(diào)用函數(shù)f()時,對變量c進行了賦值,對它們進行了自增運算后,輸出的結(jié)果都為6。在函數(shù)f()調(diào)用結(jié)束后,它們?nèi)匀淮嬖谟趦?nèi)存中,且它們的值還被保存著。當(dāng)?shù)诙握{(diào)用函數(shù)f()時,它們不會重新開辟內(nèi)存單元,所以變量d不會再次賦初值,那么它的值仍然為6,而變量c進行了一次賦值操作,它的值被賦為5,變量c和d自增后輸出的結(jié)果分別為6和7。從例7.12中可以清楚地看到局部自動變量和局部靜態(tài)變量的區(qū)別,同時可以看到對于局部靜態(tài)變量初始化和賦值會導(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));
}運行結(jié)果:
1!=1
2!=2
3!=6
4!=24
5!=120在例7.13中,每次調(diào)用函數(shù)fac(),打印出一個數(shù)的階乘,同時保留這個結(jié)果,避免了重復(fù)的運算,此時利用的就是局部靜態(tài)變量的特點。
7.5.5全局變量全局變量(也稱外部變量)是在所有函數(shù)、包括main()函數(shù)之外定義的。全局變量是存放在靜態(tài)存儲區(qū)中的,它的作用域是從全局變量定義之后直到該源文件結(jié)束的所有函數(shù);通過用extern作引用說明,全局變量的作用域可以擴大到整個程序的所有文件。在定義全局變量時可以使用static存儲類型標識符,它與普通全局變量的區(qū)別在于變量的作用域。普通全局變量不僅對文件中的所有函數(shù)都是可見的,而且能被其它文件中的函數(shù)所用;而static型的全局變量僅對其所在文件中定義處之后的函數(shù)是可見的,不能被其它文件使用。這種差別適合于程序源代碼包含在兩個或多個文件中的情況。全局變量初始化是在全局變量定義時進行的,且其初始化僅執(zhí)行一次,若無顯式初始化,則由系統(tǒng)自動初始化為與變量類型相同的0初值:整型變量初始化為整數(shù)0;
浮點型變量初始化為浮點數(shù)0.0;
字符型變量初始化為空字符′\0′。在有顯式初始化的情況下,初值必須是常量表達式。全局變量存放在靜態(tài)存儲區(qū)中,全局變量在程序執(zhí)行之前分配存儲單元,在程序運行結(jié)束后才被收回。例7.14
輸入以秒為單位的一個時間值,將其轉(zhuǎn)化成“時:分:秒”的形式輸出。將轉(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í)行時輸出:
hh=0,mm=0,ss=0
inputatimeinsecond:41574↙(輸入)
11:32:54(輸出)這里的hh、mm、ss是全局于整個程序的int類型全局變量,其初值為0,它們可以在main()函數(shù)和convertime()函數(shù)中被引用。在main()函數(shù)中,沒有對它們賦值的語句,第一個輸出語句輸出的值0是由系統(tǒng)自動賦予的初值。在convertime()中分別將轉(zhuǎn)換后的小時、分、秒賦予hh,mm,ss;從函數(shù)返回后main()函數(shù)中的輸出語句輸出的hh,mm,ss就是在convertime()函數(shù)中被賦予的值。上面的例子表明,全局變量是除參數(shù)和返回值外函數(shù)之間進行數(shù)據(jù)通信(傳遞)的又一方式。但對于一個可由任何程序調(diào)用的通用函數(shù)而言宜采用參數(shù)進行數(shù)據(jù)通信。因為參數(shù)方式只需遵守數(shù)據(jù)類型和數(shù)目上的規(guī)定,不需要實參變量與形參變量同名;用全局變量傳遞數(shù)據(jù)則要求調(diào)用函數(shù)和被調(diào)用函數(shù)必須使用相同的變量名,且不能與被調(diào)用函數(shù)的局部變量同名,這樣會降低函數(shù)的通用性,對程序的結(jié)構(gòu)化設(shè)計不利。7.5.6寄存器變量計算機的中央處理單元(CPU)中包含多個寄存器(數(shù)據(jù)存儲單元),諸如加法、除法等實際的數(shù)據(jù)操作都在其中進行。在處理數(shù)據(jù)時,CPU將數(shù)據(jù)從存儲器通過總線移到這些寄存器,處理完畢后,再將數(shù)據(jù)通過總線移回存儲器。CPU的速度可以很快,而總線的速度則要慢得多,所以運算時間主要消耗在數(shù)據(jù)從存儲器讀出或?qū)懭肷稀H绻婚_始就將一個特定變量保存在一個寄存器中,變量的處理就要快得多。C語言允許用戶向編譯程序申請,將局部自動變量保存在CPU的寄存器中而不是在常規(guī)存儲器中。在一個自動變量的定義中包含register關(guān)鍵字,其作用是請求編譯器將變量保存在一個寄存器中。寄存器變量除在可能情況下用寄存器分配存儲以及不能取地址之外,其余特性完全與自動變量相同。對于使用頻繁的值,使用寄存器變量可以提高程序運行速度。下面的例子可說明寄存器變量的用法。
例7.15
計算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í)行時輸出:
Inputx,n:45
s=1364其中4和5分別為鍵入的x和n。計算機的寄存器是有限的,為確保寄存器用于最需要的地方,應(yīng)將使用最頻繁的值說明為寄存器存儲類型,通常用于使用最頻繁的整型或字符型值。說明為寄存器存儲類型的局部變量首先在寄存器中分配存儲,如果無足夠的寄存器,則和自動變量一樣在內(nèi)存中分配存儲。目前,大部分編譯系統(tǒng)(如TC、VisualC++等)都不支持真正的register變量,而仍然把register變量保存在存儲器中,因此,一般不必使用register變量。7.6編譯預(yù)處理
C語言提供編譯預(yù)處理的功能,這是它與其它高級語言的一個重要區(qū)別。在C編譯系統(tǒng)程序進行通常的編譯前,先對程序中這些特殊的命令進行“預(yù)處理”,然后將預(yù)處理的結(jié)果和源程序一起再進行通常的編譯處理,以得到目標代碼。C語言通過預(yù)處理程序提供了一些語言功能,預(yù)處理程序從理論上講是編譯過程中單獨進行的第一個步驟。
C語言提供的預(yù)處理功能主要有三種,即宏定義、文件包含和條件編譯,分別用宏定義命令、文件包含命令、條件編譯命令來實現(xiàn)。為了與一般C語句區(qū)別,這些命令以符號“#”開頭。7.6.1宏定義宏定義即#define指令,具有如下形式:
#define名字替換文本它是一種最簡單的宏替換——出現(xiàn)各自的名字都將被替換文本替換。宏定義是由源程序中的宏定義命令完成的。宏替換是由預(yù)處理程序自動完成的。在#語言中,“宏”分為有參數(shù)和無參數(shù)兩種。下面分別討論這兩種“宏”的定義和調(diào)用。
1.不帶參數(shù)的宏定義用一個指定的標識符(即名字)來代表一個字符串,它的一般形式為:
#define標識符字符串其中的“#”表示這是一條預(yù)處理命令。凡是以“#”開頭的均為預(yù)處理命令?!癲efine”為宏定義命令。“標識符”為所定義的宏名。“字符串”可以是常數(shù)、表達式、格式串等。正常情況下,替換文本是#define指令所在行的剩余部分,但也可以把一個比較長的宏定義分成若干行,這時只需在尚待延續(xù)的行后加上一個反斜杠“\”即可。常對程序中反復(fù)使用的表達式進行宏定義。例如:
#defineM(y*y+3*y)定義M表達式(y*y+3*y)。在編寫源程序時,所有的(y*y+3*y)都可由M代替,而對源程序作編譯時,將先由預(yù)處理程序進行宏代換,即用(y*y+3*y)表達式去置換所有的宏名M,然后再進行編譯。例7.16
用宏定義計算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);
}上例程序中首先進行宏定義,定義M表達式(y*y+3*y),在s=3*M+4*M+5*M中作了宏替換。在預(yù)處理時經(jīng)宏展開后該語句變?yōu)椋簊=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y)。
說明:
(1)宏名一般習(xí)慣用大寫字母表示,以與變量名區(qū)別。但這并非規(guī)定,也可用小寫字母。
(2)使用宏名代替一個字符串,可以減少程序中重復(fù)書寫某些字符串的工作量。
(3)宏定義是用宏名代替一個字符串,也就是作簡單的置換,不作語法檢查。
(4)宏定義不是C語句,不必在行末加分號,如果加了分號則會連分號一起進行置換。
(5)#define命令出現(xiàn)在程序中函數(shù)的外面,宏名的有效范圍為定義命令之后到本源文件結(jié)束。通常,#define命令寫在文件開頭、函數(shù)之前,作為文件的一部分,在文件范圍內(nèi)有效。
(6)可以用#undef命令終止宏定義的作用域。例如:
#definePI3.14159
voidmain()
{
}
#undefPI的作用域
voidf1()
{
}表示PI只在main()函數(shù)中有效,在f1()中無效。
(7)在進行宏定義時,可以引用已定義的宏名,并層層替換。
(8)對程序中用雙引號括起來的字符,即使與宏名相同,也不進行替換。例如:
#include<stdio.h>
#defineOK100
voidmain()
{
printf("OK");
}定義宏名OK表示100,但在printf語句中OK被引號括起來,因此不作宏代換。程序的運行結(jié)果為:OK。這表示把“OK”當(dāng)字符串處理。
2.帶參數(shù)的宏定義帶參數(shù)的宏定義不僅是進行簡單的字符串替換,還要進行參數(shù)替換。其定義的一般形式為:
#define宏名(參數(shù)表)字符串字符串中包含在括弧中所指的參數(shù)。如:
#defineS(a,b)a*b
area=S(3,2);對帶參的宏定義是這樣展開置換的:在程序中如果有帶實參的宏(如S(3,2)),則#define命令行中的字符串從左到右進行置換。如果串中包含宏中的形參(如(a,b)),則將程序語句中相應(yīng)的實參(可以是常量、變量或表達式)代替形參,如果宏定義中的字符串中的字符不是參數(shù)字符(如a*b中的*號),則保留。這樣就形成了字符串,見圖7.4。圖7.4帶參數(shù)的宏定義的置換說明:
(1)對帶參的宏展開只是將語句中的宏名后面的括號內(nèi)的實參字符串代替#define命令行中的形參。
(2)在宏定義時,在宏名與帶參的括號之間不應(yīng)加空格,否則將空格以后的字符串都作為代替字符串的一部分。
(3)定義宏時,最好將參數(shù)和宏體用括號括起來。如:#definesquare(n)n*n,調(diào)用時s=square(a+1),則變成了s=a+1*a+1,與預(yù)期效果不同。
7.6.2文件包含處理所謂的文件包含處理,是指一個源文件可以將另外一個源文件的全部內(nèi)容包含進來,即將另外的文件包含到本文件之中。C語言提供了#include命令來實現(xiàn)文件包含的操作。其一般形式為:
#include"文件名"例如:
#include"stdio.h"
#include"math.h"圖7.5表示文件包含的含意。圖7.5(a)為文件file1.c,它有一個#include<file2.c>命令,而且還有其它內(nèi)容(以A表示)。圖7.5(b)為另一文件file2.c,文件內(nèi)容以B表示。在編譯處理時,要對#include命令進行文件包含處理:將file2.c的全部內(nèi)容復(fù)制插入到include<file2.c>命令處,即file2.c被復(fù)制到file1.c中,得到圖7.5(c)所示的結(jié)果。在編譯中,將“包含”以后的file1.c(即圖7.5(c)所示)作為一個源文件單獨進行編譯。圖7.5文件包含用在文件頭部的被包含的文件稱為標題文件或頭部文件,常以“.h”為后綴。如果需要修改一些常數(shù),不必修改每個程序,只需修改一個文件(頭部文件)即可。頭部文件除了可以包括宏定義外,也可以包括結(jié)構(gòu)體類型定義、全局變量定義等。
說明:
(1)一個#include命令只能指定一個被包含
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年黑龍江道路貨運駕駛員從業(yè)資格證考試題庫
- 服裝公司總經(jīng)理聘用合同模板
- 工程監(jiān)理承包合同
- 農(nóng)村考古遺址考古旅游開發(fā)合同
- 社區(qū)服務(wù)管理分層管理辦法
- 2025勞動合同不續(xù)簽處理
- 2024年度高品質(zhì)鈦礦出口貿(mào)易合同3篇
- 2024年物業(yè)管理招標申請文件3篇
- 陶藝館租賃合同
- 食品文件生產(chǎn)流程
- 公路養(yǎng)護資質(zhì)標準匯編整理
- AFC1500擰緊控制器
- GB_T 37515-2019 再生資源回收體系建設(shè)規(guī)范(高清版)
- 商品條碼管理辦法條文釋義
- 八年級上冊歷史知識結(jié)構(gòu)圖
- 特殊建設(shè)工程消防設(shè)計審查申請表
- 漢密爾頓抑郁量表(24項)——評定方法
- 功能高分子材料和智能高分子材料.PPT
- 莫爾條紋干涉光學(xué)系統(tǒng)仿真設(shè)計
- 紅外熱像儀的應(yīng)用PPT課件
- 未婚承諾書模板
評論
0/150
提交評論