?? 8.txt
字號:
8 函 數 1
8.1 概述 1
8.2 函數定義的一般形式 3
8.3 函數的參數和函數的值 4
8.3.1 形式參數和實際參數 4
8.3.2 函數的返回值 5
8.4 函數的調用 6
8.4.1 函數調用的一般形式 6
8.4.2 函數調用的方式 6
8.4.3 被調用函數的聲明和函數原型 7
8.5 函數的嵌套調用 8
8.6 函數的遞歸調用 10
8.7 數組作為函數參數 12
8.8 局部變量和全局變量 17
8.8.1 局部變量 17
8.8.2 全局變量 19
8.9 變量的存儲類別 20
8.9.1 動態存儲方式與靜態動態存儲方式 20
8.9.2 auto變量 21
8.9.3 用static聲明局部變量 21
8.9.4 register變量 22
8.9.5 用extern聲明外部變量 23
8 函 數
8.1 概述
在前面已經介紹過,C源程序是由函數組成的。雖然在前面各章的程序中大都只有一個主函數main(),但實用程序往往由多個函數組成。函數是C源程序的基本模塊,通過對函數模塊的調用實現特定的功能。C語言中的函數相當于其它高級語言的子程序。C語言不僅提供了極為豐富的庫函數(如Turbo C,MS C都提供了三百多個庫函數),還允許用戶建立自己定義的函數。用戶可把自己的算法編成一個個相對獨立的函數模塊,然后用調用的方法來使用函數??梢哉fC程序的全部工作都是由各式各樣的函數完成的,所以也把C語言稱為函數式語言。
由于采用了函數模塊式的結構,C語言易于實現結構化程序設計。使程序的層次結構清晰,便于程序的編寫、閱讀、調試。
在C語言中可從不同的角度對函數分類。
1. 從函數定義的角度看,函數可分為庫函數和用戶定義函數兩種。
1) 庫函數:由C系統提供,用戶無須定義,也不必在程序中作類型說明,只需在程序前包含有該函數原型的頭文件即可在程序中直接調用。在前面各章的例題中反復用到printf、scanf、getchar、putchar、gets、puts、strcat等函數均屬此類。
2) 用戶定義函數:由用戶按需要寫的函數。對于用戶自定義函數,不僅要在程序中定義函數本身,而且在主調函數模塊中還必須對該被調函數進行類型說明,然后才能使用。
2. C語言的函數兼有其它語言中的函數和過程兩種功能,從這個角度看,又可把函數分為有返回值函數和無返回值函數兩種。
1) 有返回值函數:此類函數被調用執行完后將向調用者返回一個執行結果,稱為函數返回值。如數學函數即屬于此類函數。由用戶定義的這種要返回函數值的函數,必須在函數定義和函數說明中明確返回值的類型。
2) 無返回值函數:此類函數用于完成某項特定的處理任務,執行完成后不向調用者返回函數值。這類函數類似于其它語言的過程。由于函數無須返回值,用戶在定義此類函數時可指定它的返回為“空類型”, 空類型的說明符為“void”。
3. 從主調函數和被調函數之間數據傳送的角度看又可分為無參函數和有參函數兩種。
1) 無參函數:函數定義、函數說明及函數調用中均不帶參數。主調函數和被調函數之間不進行參數傳送。此類函數通常用來完成一組指定的功能,可以返回或不返回函數值。
2) 有參函數:也稱為帶參函數。在函數定義及函數說明時都有參數,稱為形式參數(簡稱為形參)。在函數調用時也必須給出參數,稱為實際參數(簡稱為實參)。進行函數調用時,主調函數將把實參的值傳送給形參,供被調函數使用。
4. C語言提供了極為豐富的庫函數,這些庫函數又可從功能角度作以下分類。
1) 字符類型分類函數:用于對字符按ASCII碼分類:字母,數字,控制字符,分隔符,大小寫字母等。
2) 轉換函數:用于字符或字符串的轉換;在字符量和各類數字量(整型,實型等)之間進行轉換;在大、小寫之間進行轉換。
3) 目錄路徑函數:用于文件目錄和路徑操作。
4) 診斷函數:用于內部錯誤檢測。
5) 圖形函數:用于屏幕管理和各種圖形功能。
6) 輸入輸出函數:用于完成輸入輸出功能。
7) 接口函數:用于與DOS,BIOS和硬件的接口。
8) 字符串函數:用于字符串操作和處理。
9) 內存管理函數:用于內存管理。
10) 數學函數:用于數學函數計算。
11) 日期和時間函數:用于日期,時間轉換操作。
12) 進程控制函數:用于進程管理和控制。
13) 其它函數:用于其它各種功能。
以上各類函數不僅數量多,而且有的還需要硬件知識才會使用,因此要想全部掌握則需要一個較長的學習過程。應首先掌握一些最基本、最常用的函數,再逐步深入。由于課時關系,我們只介紹了很少一部分庫函數,其余部分讀者可根據需要查閱有關手冊。
還應該指出的是,在C語言中,所有的函數定義,包括主函數main在內,都是平行的。也就是說,在一個函數的函數體內,不能再定義另一個函數,即不能嵌套定義。但是函數之間允許相互調用,也允許嵌套調用。習慣上把調用者稱為主調函數。函數還可以自己調用自己,稱為遞歸調用。
main 函數是主函數,它可以調用其它函數,而不允許被其它函數調用。因此,C程序的執行總是從main函數開始,完成對其它函數的調用后再返回到main函數,最后由main函數結束整個程序。一個C源程序必須有,也只能有一個主函數main。
8.2 函數定義的一般形式
1. 無參函數的定義形式
類型標識符 函數名()
{聲明部分
語句
}
其中類型標識符和函數名稱為函數頭。類型標識符指明了本函數的類型,函數的類型實際上是函數返回值的類型。 該類型標識符與前面介紹的各種說明符相同。函數名是由用戶定義的標識符,函數名后有一個空括號,其中無參數,但括號不可少。
{}中的內容稱為函數體。在函數體中聲明部分,是對函數體內部所用到的變量的類型說明。
在很多情況下都不要求無參函數有返回值,此時函數類型符可以寫為void。
我們可以改寫一個函數定義:
void Hello()
{
printf ("Hello,world \n");
}
這里,只把main改為Hello作為函數名,其余不變。Hello函數是一個無參函數,當被其它函數調用時,輸出Hello world字符串。
2. 有參函數定義的一般形式
類型標識符 函數名(形式參數表列)
{聲明部分
語句
}
有參函數比無參函數多了一個內容,即形式參數表列。在形參表中給出的參數稱為形式參數,它們可以是各種類型的變量,各參數之間用逗號間隔。在進行函數調用時,主調函數將賦予這些形式參數實際的值。形參既然是變量,必須在形參表中給出形參的類型說明。
例如,定義一個函數,用于求兩個數中的大數,可寫為:
int max(int a, int b)
{
if (a>b) return a;
else return b;
}
第一行說明max函數是一個整型函數,其返回的函數值是一個整數。形參為a,b,均為整型量。a,b的具體值是由主調函數在調用時傳送過來的。在{}中的函數體內,除形參外沒有使用其它變量,因此只有語句而沒有聲明部分。在max函數體中的return語句是把a(或b)的值作為函數的值返回給主調函數。有返回值函數中至少應有一個return語句。
在C程序中,一個函數的定義可以放在任意位置,既可放在主函數main之前,也可放在main之后。
例如:
可把max 函數置在main之后,也可以把它放在main之前。修改后的程序如下所示。
【例8.1】
int max(int a,int b)
{
if(a>b)return a;
else return b;
}
main()
{
int max(int a,int b);
int x,y,z;
printf("input two numbers:\n");
scanf("%d%d",&x,&y);
z=max(x,y);
printf("maxmum=%d",z);
}
現在我們可以從函數定義、函數說明及函數調用的角度來分析整個程序,從中進一步了解函數的各種特點。
程序的第1行至第5行為max函數定義。進入主函數后,因為準備調用max函數,故先對max函數進行說明(程序第8行)。函數定義和函數說明并不是一回事,在后面還要專門討論。 可以看出函數說明與函數定義中的函數頭部分相同,但是末尾要加分號。程序第12 行為調用max函數,并把x, y中的值傳送給max的形參a, b。max函數執行的結果(a或b)將返回給變量z。最后由主函數輸出z的值。
8.3 函數的參數和函數的值
8.3.1 形式參數和實際參數
前面已經介紹過,函數的參數分為形參和實參兩種。在本小節中,進一步介紹形參、實參的特點和兩者的關系。形參出現在函數定義中,在整個函數體內都可以使用,離開該函數則不能使用。實參出現在主調函數中,進入被調函數后,實參變量也不能使用。形參和實參的功能是作數據傳送。發生函數調用時,主調函數把實參的值傳送給被調函數的形參從而實現主調函數向被調函數的數據傳送。
函數的形參和實參具有以下特點:
1. 形參變量只有在被調用時才分配內存單元,在調用結束時,即刻釋放所分配的內存單元。因此,形參只有在函數內部有效。函數調用結束返回主調函數后則不能再使用該形參變量。
2. 實參可以是常量、變量、表達式、函數等,無論實參是何種類型的量,在進行函數調用時,它們都必須具有確定的值,以便把這些值傳送給形參。因此應預先用賦值,輸入等辦法使實參獲得確定值。
3. 實參和形參在數量上,類型上,順序上應嚴格一致,否則會發生類型不匹配”的錯誤。
4. 函數調用中發生的數據傳送是單向的。即只能把實參的值傳送給形參,而不能把形參的值反向地傳送給實參。 因此在函數調用過程中,形參的值發生改變,而實參中的值不會變化。
【例8.2】可以說明這個問題。
main()
{
int n;
printf("input number\n");
scanf("%d",&n);
s(n);
printf("n=%d\n",n);
}
int s(int n)
{
int i;
for(i=n-1;i>=1;i--)
n=n+i;
printf("n=%d\n",n);
}
本程序中定義了一個函數s,該函數的功能是求∑ni的值。在主函數中輸入n值,并作為實參,在調用時傳送給s 函數的形參量n( 注意,本例的形參變量和實參變量的標識符都為n,但這是兩個不同的量,各自的作用域不同)。在主函數中用printf 語句輸出一次n值,這個n值是實參n的值。在函數s中也用printf 語句輸出了一次n值,這個n值是形參最后取得的n值0。從運行情況看,輸入n值為100。即實參n的值為100。把此值傳給函數s時,形參n的初值也為100,在執行函數過程中,形參n的值變為5050。返回主函數之后,輸出實參n的值仍為100??梢妼崊⒌闹挡浑S形參的變化而變化。
8.3.2 函數的返回值
函數的值是指函數被調用之后,執行函數體中的程序段所取得的并返回給主調函數的值。如調用正弦函數取得正弦值,調用例8.1的max函數取得的最大數等。對函數的值(或稱函數返回值)有以下一些說明:
1) 函數的值只能通過return語句返回主調函數。
return 語句的一般形式為:
return 表達式;
或者為:
return (表達式);
該語句的功能是計算表達式的值,并返回給主調函數。在函數中允許有多個return語句,但每次調用只能有一個return 語句被執行,因此只能返回一個函數值。
2) 函數值的類型和函數定義中函數的類型應保持一致。如果兩者不一致,則以函數類型為準,自動進行類型轉換。
3) 如函數值為整型,在函數定義時可以省去類型說明。
4) 不返回函數值的函數,可以明確定義為“空類型”,類型說明符為“void”。如例8.2中函數s并不向主函數返函數值,因此可定義為:
void s(int n)
{ ……
}
一旦函數被定義為空類型后,就不能在主調函數中使用被調函數的函數值了。例如,在定義s為空類型后,在主函數中寫下述語句
sum=s(n);
就是錯誤的。
為了使程序有良好的可讀性并減少出錯, 凡不要求返回值的函數都應定義為空類型。
8.4 函數的調用
8.4.1 函數調用的一般形式
前面已經說過,在程序中是通過對函數的調用來執行函數體的,其過程與其它語言的子程序調用相似。
C語言中,函數調用的一般形式為:
函數名(實際參數表)
對無參函數調用時則無實際參數表。實際參數表中的參數可以是常數,變量或其它構造類型數據及表達式。各實參之間用逗號分隔。
8.4.2 函數調用的方式
在C語言中,可以用以下幾種方式調用函數:
1. 函數表達式:函數作為表達式中的一項出現在表達式中,以函數返回值參與表達式的運算。這種方式要求函數是有返回值的。例如:z=max(x,y)是一個賦值表達式,把max的返回值賦予變量z。
2. 函數語句:函數調用的一般形式加上分號即構成函數語句。例如: printf ("%d",a);scanf ("%d",&b);都是以函數語句的方式調用函數。
3. 函數實參:函數作為另一個函數調用的實際參數出現。這種情況是把該函數的返回值作為實參進行傳送,因此要求該函數必須是有返回值的。例如: printf("%d",max(x,y)); 即是把max調用的返回值又作為printf函數的實參來使用的。在函數調用中還應該注意的一個問題是求值順序的問題。所謂求值順序是指對實參表中各量是自左至右使用呢,還是自右至左使用。對此,各系統的規定不一定相同。介紹printf 函數時已提到過,這里從函數調用的角度再強調一下。
【例8.3】
main()
{
int i=8;
printf("%d\n%d\n%d\n%d\n",++i,--i,i++,i--);
}
如按照從右至左的順序求值。運行結果應為:
8
7
7
8
如對printf語句中的++i,--i,i++,i--從左至右求值,結果應為:
9
8
8
9
應特別注意的是,無論是從左至右求值, 還是自右至左求值,其輸出順序都是不變的, 即輸出順序總是和實參表中實參的順序相同。由于Turbo C現定是自右至左求值,所以結果為8,7,7,8。上述問題如還不理解,上機一試就明白了。
8.4.3 被調用函數的聲明和函數原型
在主調函數中調用某函數之前應對該被調函數進行說明(聲明),這與使用變量之前要先進行變量說明是一樣的。在主調函數中對被調函數作說明的目的是使編譯系統知道被調函數返回值的類型,以便在主調函數中按此種類型對返回值作相應的處理。
其一般形式為:
類型說明符 被調函數名(類型 形參,類型 形參…);
或為:
類型說明符 被調函數名(類型,類型…);
括號內給出了形參的類型和形參名,或只給出形參類型。這便于編譯系統進行檢錯,以防止可能出現的錯誤。
例8.1 main函數中對max函數的說明為:
int max(int a,int b);
或寫為:
int max(int,int);
C語言中又規定在以下幾種情況時可以省去主調函數中對被調函數的函數說明。
1) 如果被調函數的返回值是整型或字符型時,可以不對被調函數作說明,而直接調用。這時系統將自動對被調函數返回值按整型處理。例8.2的主函數中未對函數s作說明而直接調用即屬此種情形。
2) 當被調函數的函數定義出現在主調函數之前時,在主調函數中也可以不對被調函數再作說明而直接調用。例如例8.1中,函數max的定義放在main 函數之前,因此可在main函數中省去對max函數的函數說明int max(int a,int b)。
3) 如在所有函數定義之前,在函數外預先說明了各個函數的類型,則在以后的各主調函數中,可不再對被調函數作說明。例如:
char str(int a);
float f(float b);
main()
{
……
}
char str(int a)
{
……
}
float f(float b)
{
……
}
其中第一,二行對str函數和f函數預先作了說明。因此在以后各函數中無須對str和f函數再作說明就可直接調用。
4) 對庫函數的調用不需要再作說明,但必須把該函數的頭文件用include命令包含在源文件前部。
8.5 函數的嵌套調用
C語言中不允許作嵌套的函數定義。因此各函數之間是平行的,不存在上一級函數和下一級函數的問題。但是C語言允許在一個函數的定義中出現對另一個函數的調用。這樣就出現了函數的嵌套調用。即在被調函數中又調用其它函數。這與其它語言的子程序嵌套的情形是類似的。其關系可表示如圖。
圖表示了兩層嵌套的情形。其執行過程是:執行main函數中調用a函數的語句時,即轉去執行a函數,在a函數中調用b 函數時,又轉去執行b函數,b函數執行完畢返回a函數的斷點繼續執行,a函數執行完畢返回main函數的斷點繼續執行。
【例8.4】計算s=22!+32!
本題可編寫兩個函數,一個是用來計算平方值的函數f1,另一個是用來計算階乘值的函數f2。主函數先調f1計算出平方值,再在f1中以平方值為實參,調用 f2計算其階乘值,然后返回f1,再返回主函數,在循環程序中計算累加和。
long f1(int p)
{
int k;
long r;
long f2(int);
k=p*p;
r=f2(k);
return r;
}
long f2(int q)
{
long c=1;
int i;
for(i=1;i<=q;i++)
c=c*i;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -