C語言代碼由上到下依次執行,原則上函數定義要出現在函數調用之前,否則就會報錯。但在實際開發中,經常會在函數定義之前使用它們,這個時候就需要提前聲明。
所謂聲明(Declaration),就是告訴編譯器我要使用這個函數,你現在沒有找到它的定義不要緊,請不要報錯,稍後我會把定義補上。
函數聲明的格式非常簡單,相當于去掉函數定義中的函數體,并在最後加上分号;,如下所示:
dataType functionName( dataType1 param1, dataType2 param2 ... );
也可以不寫形參,隻寫資料類型:
dataType functionName( dataType1, dataType2 ... );
函數聲明給出了函數名、傳回值類型、參數清單(重點是參數類型)等與該函數有關的資訊,稱為函數原型(Function Prototype)。函數原型的作用是告訴編譯器與該函數有關的資訊,讓編譯器知道函數的存在,以及存在的形式,即使函數暫時沒有定義,編譯器也知道如何使用它。
有了函數聲明,函數定義就可以出現在任何地方了,甚至是其他檔案、靜态連結庫、動态連結庫等。
【執行個體1】定義一個函數 sum(),計算從 m 加到 n 的和,并将 sum() 的定義放到 main() 後面。
#include <stdio.h>
//函數聲明
int sum(int m, int n); //也可以寫作int sum(int, int);
int main(){
int begin = 5, end = 86;
int result = sum(begin, end);
printf("The sum from %d to %d is %d\n", begin, end, result);
return 0;
}
//函數定義
int sum(int m, int n){
int i, sum=0;
for(i=m; i<=n; i++){
sum+=i;
}
return sum;
}
我們在 main() 函數中調用了 sum() 函數,編譯器在它前面雖然沒有發現函數定義,但是發現了函數聲明,這樣編譯器就知道函數怎麼使用了,至于函數體到底是什麼,暫時可以不用操心,後續再把函數體補上就行。
【執行個體2】定義兩個函數,計算1! + 2! + 3! + ... + (n-1)! + n!的和。
#include <stdio.h>
// 函數聲明部分
long factorial(int n); //也可以寫作 long factorial(int);
long sum(long n); //也可以寫作 long sum(long);
int main(){
printf("1!+2!+...+9!+10! = %ld\n", sum(10));
return 0;
}
//函數定義部分
//求階乘
long factorial(int n){
int i;
long result=1;
for(i=1; i<=n; i++){
result *= i;
}
return result;
}
// 求累加的和
long sum(long n){
int i;
long result = 0;
for(i=1; i<=n; i++){
result += factorial(i);
}
return result;
}
運作結果:
1!+2!+...+9!+10! = 4037913
初學者編寫的代碼都比較簡單,頂多幾百行,完全可以放在一個源檔案中。對于單個源檔案的程式,通常是将函數定義放到 main() 的後面,将函數聲明放到 main() 的前面,這樣就使得代碼結構清晰明了,主次分明。
使用者往往隻關心函數的功能和函數的調用形式,很少關心函數的實作細節,将函數定義放在最後,就是盡量屏蔽不重要的資訊,凸顯關鍵的資訊。将函數聲明放到 main() 的前面,在定義函數時也不用關注它們的調用順序了,哪個函數先定義,哪個函數後定義,都無所謂了。
然而在實際開發中,往往都是幾千行、上萬行、百萬行的代碼,将這些代碼都放在一個源檔案中簡直是災難,不但檢索麻煩,而且打開檔案也很慢,是以必須将這些代碼分散到多個檔案中。對于多個檔案的程式,通常是将函數定義放到源檔案(.c檔案)中,将函數的聲明放到頭檔案(.h檔案)中,使用函數時引入對應的頭檔案就可以,編譯器會在連結階段找到函數體。
前面我們在使用 printf()、puts()、scanf() 等函數時引入了 stdio.h 頭檔案,很多初學者認為 stdio.h 中包含了函數定義(也就是函數體),隻要有了頭檔案就能運作,其實不然,頭檔案中包含的都是函數聲明,而不是函數定義,函數定義都放在了其它的源檔案中,這些源檔案已經提前編譯好了,并以動态連結庫或者靜态連結庫的形式存在,隻有頭檔案沒有系統庫的話,在連結階段就會報錯,程式根本不能運作。
關于編譯連結的原理,以及如果将代碼分散到多個檔案中,我們将在《C語言多檔案程式設計》專題中詳細講解。
除了函數,變量也有定義和聲明之分。實際開發過程中,變量定義需要放在源檔案(.c檔案)中,變量聲明需要放在頭檔案(.h檔案)中,在連結程式時會将它們對應起來,這些我們也将在《C語言多檔案程式設計》專題中詳細講解。
學完《C語言多檔案程式設計》,你對C語言的認識将會有質的提升,瞬間豁然開朗,輕松超越 90% 的C語言程式員。
函數參考手冊
最後再補充一點,函數原型給出了使用該函數的所有細節,當我們不知道如何使用某個函數時,需要查找的是它的原型,而不是它的定義,我們往往不關心它的實作。
www.cplusplus.com 是一個非常給力的網站,它提供了所有C語言标準函數的原型,并給出了詳細的介紹和使用示例,可以作為一部權威的參考手冊。