山不在高,有仙則名。水不在深,有龍則靈。 ----CSDN 時時三省
在一個函數中調用另一個函數(即被調用函數)需要具備如下條件
( 1 )首先被調用的函數必須是已經定義的函數(是庫函數或用戶自己定義的函數),但僅有這一條件還不夠。
( 2 )如果使用庫函數,應該在本文件開頭用# include指令將調用有關庫函數時所需用到的信息“包含”到本文件中來。
例如:
# include < stdio.h >
其中,“stdio.h”是一個“頭文件”。在stdio.h文件中包含了輸入輸出庫函數的聲明。如果不包含“stdio.h”文件,就無法使用輸入輸出庫中的函數。同樣,使用數學庫中的函數,應該用# include < math.h >。h是頭文件所用的后綴,表示是頭文件(headerfile)。
( 3 )如果使用用戶自己定義的函數,而該函數的位置在調用它的函數(即主調函數)的后面(在同一個文件中),應該在主調函數中對被調用的函數作聲明(declaration)。聲明的作用是把函數名、函數參數的個數和參數類型等信息通知編譯系統,以便在遇到函數調用時,編譯系統能正確識別函數并檢查調用是否合法。
例題:
輸入兩個實數,用一個函數求出它們之和。
解題思路:
兩個數相加的算法很簡單。現在用add函數實現它。首先要定義add函數,它為float型,它應有兩個參數,也應為float型。特別要注意的是:要對add函數進行聲明。
編寫程序:
運行結果:?
?
這是一個很簡單的函數調用,函數add的作用是求兩個實數之和,得到的函數值也是實型。程序第3行是對被調用的add函數作聲明:
float add ( float x , float y ) ;
從程序可以看到:main函數的位置在add函數的前面,而程序進行編譯時是從上到下逐行進行的,如果沒有對函數add的聲明,當編譯到程序第7行時,編譯系統無法確定add是不是函數名,也無法判斷實參( a和b )的類型和個數是否正確,因而無法進行正確性的檢查。
如果不作檢查,在運行時才發現實參與形參的類型或個數不一致,出現運行錯誤。但是在運行階段發現錯誤并重新調試程序,是比較麻煩的,工作量也較大。應當在編譯階段盡可能多地發現錯誤,隨之糾正錯誤。
現在,在函數調用之前對add作了函數聲明。因此編譯系統記下了add函數的有關信息,在對“c = add ( a,b );”進行編譯時就“有章可循”了。編譯系統根據add函數的聲明對調用add函數的合法性進行全面的檢查。如果發現函數調用與函數聲明不匹配,就會發出出錯信息,它屬于語法錯誤。用戶根據屏幕顯示的出錯信息很容易發現和糾正錯誤。
可以發現,函數的聲明和函數定義中的第1行(函數首部)基本上是相同的,只差一個分號(函數聲明比函數定義中的首行多一個分號)。因此寫函數聲明時,可以簡單地照寫已定義的函數的首行,再加一個分號,就成了函數的“聲明”。
函數的首行(即函數首部)稱為函數原型( function prototype )。為什么要用函數的首部來作為函數聲明呢?這是為了便于對函數調用的合法性進行檢查。因為在函數的首部包含了檢查調用函數是否合法的基本信息(它包括了函數名、函數值類型、參數個數、參數類型和參數順序),在檢查函數調用時要求函數名、函數類型、參數個數和參數順序必須與函數聲明一致,實參類型必須與函數聲明中的形參類型相同(或賦值兼容,如實型數據可以傳遞給整型形參,按賦值規則進行類型轉換)。否則就按出錯處理。這樣就能保證函數的正確調用。
說明:
使用函數原型作聲明是C的一個重要特點。用函數原型來聲明函數,能減少編寫程序時可能出現的錯誤。由于函數聲明的位置與函數調用語句的位置比較近,因此在寫程序時便于就近參照函數原型來書寫函數調用,不易出錯。實際上,在函數聲明中的形參名可以省寫,而只寫形參的類型,如上面的聲明可以寫為
float add ( float , float );/ /不寫參數名,只寫參數類型
編譯系統只關心和檢查參數個數和參數類型,而不檢查參數名,因為在調用函數時只要求保證實參類型與形參類型一致,而不必考慮形參名是什么。因此在函數聲明中,形參名可寫可不寫,形參名是什么都無所謂,如:
float add ( float a , float b );/ /參數名不用x,y,而用a,b。合法
根據以上的介紹,函數聲明的一般形式有兩種,分別為
( 1 )函數類型函數名(參數類型1參數名1,參數類型2參數名2 ,...,參數類型n參數名n );
( 2 )函數類型函數名(參數類型1,參數類型2,...,參數類型n );
有些專業人員喜歡用不寫參數名的第(2 )種形式,顯得精練。有些人則愿意用第(1 )種形式,只須照抄函數首部就可以了,不易出錯,而且用了有意義的參數名有利于理解程序,如:
void print ( int num , char sex , float score );
大體上可猜出這是一個輸出學號、性別和成績的函數,而若寫成
void print ( int , float , char ) ;
則無從知道形參的含義。
注意:
對函數的“定義”和“聲明”不是同一回事。函數的定義是指對函數功能的確立,包括指定函數名、函數值類型、形參及其類型以及函數體等,它是一個完整的、獨立的函數單位。而函數的聲明的作用則是把函數的名字、函數類型以及形參的類型、個數和順序通知編譯系統,以便在調用該函數時系統按此進行對照檢查(例如,函數名是否正確,實參與形參的類型和個數是否一致),它不包含函數體。
如果已在文件的開頭(在所有函數之前),已對本文件中所調用的函數進行了聲明,則在各函數中不必對其所調用的函數再作聲明。例如:
char letter ( char , char ) ;
float f ( float , float ) ;
int i ( float , float ) ;
int main ( )
{
...
}
/ /下面定義被main函數調用的3個函數
char letter ( char c1 , char c2 )
{
...
}
float f ( float x , float y )
{
...
}
int i ( float j , float k )
{
...
}
由于在文件的開頭(在函數的外部)已對要調用的函數進行了聲明(這些稱為“外部的聲明”),因此在程序編時,編譯系統已從外部聲明中知道了函數的有關信息,所以不必在主調函數中再重復進行聲明。寫在所有函數前面的外部聲明在整個文件范圍中有效。
?
?