本節介紹C程序的基本單元--函數。函數中包含了程序的可執行代碼。每個C程序的入口和出口都位於函數main()之中。main()函數可以調用其他函數,這些函數執行完畢後程序的控制又返回到main()函數中,main()函數不能被別的函數所調用。通常我們把這些被調用的函數稱為下層(lower-level)函數。函數調用發生時,立即執行被調用的函數,而調用者則進入等待狀態,直到被調用函數執行完畢。函數可以有參數和返回值。
程序員一般把函數當作“黑箱”處理,並不關心它內部的實現細節。當然程序員也可以自己開發函數庫。
說明一點,函數這一節很重要,可以說一個程序的優劣集中體現在函數上。如果函數使用的恰當,可以讓程序看起來有條理,容易看懂。如果函數使用的亂七八糟,或者是沒有使用函數,程序就會顯得很亂,不僅讓別人無法查看,就連自己也容易暈頭轉向。可以這樣說,如果超過100行的程序中沒有使用函數,那麼這個程序一定很羅嗦(有些絕對,但也是事實)。
一、函數的定義
一個函數包括函數頭和語句體兩部分。
函數頭由下列三不分組成:
函數返回值類型
函數名
參數表
一個完整的函數應該是這樣的:
函數返回值類型 函數名(參數表)
{
語句體;
}
函數返回值類型可以是前面說到的某個數據類型、或者是某個數據類型的指針、指向結構的指針、指向數組的指針。指針概念到以後再介紹。
函數名在程序中必須是唯一的,它也遵循標識符命名規則。
參數表可以沒有也可以有多個,在函數調用的時候,實際參數將被拷貝到這些變量中。語句體包括局部變量的聲明和可執行代碼。
我們在前面其實已經接觸過函數了,如abs(),sqrt(),我們並不知道它的內部是什麼,我們只要會使用它即可。
這一節主要講解無參數無返回值的函數調用。
二、函數的聲明和調用
為了調用一個函數,必須事先聲明該函數的返回值類型和參數類型,這和使用變量的道理是一樣的(有一種可以例外,就是函數的定義在調用之前,下面再講述)。
看一個簡單的例子:
void a(); /*函數聲明*/
main()
{
a(); /*函數調用*/
}
void a() /*函數定義*/
{
int num;
scanf(%d,&num);
printf(%d\n,num);
}
在main()的前面聲明了一個函數,函數類型是void型,函數名為a,無參數。然後在main()函數裡面調用這個函數,該函數的作用很簡單,就是輸入一個整數然後再顯示它。在調用函數之前聲明了該函數其實它和下面這個程序的功能是一樣的:
main()
{
int num;
scanf(%d,&num);
printf(%d\n,num);
}
可以看出,實際上就是把a()函數裡面的所有內容直接搬到main()函數裡面(注意,這句話不是絕對的。)
我們前面已經說了,當定義在調用之前時,可以不聲明函數。所以上面的程序和下面這個也是等價的:
void a()
{
int num;
scanf(%d,&num);
printf(%d\n,num);
}
main()
{
a();
}
因為定義在調用之前,所以可以不聲明函數,這是因為編譯器在編譯的時候,已經發現a是一個函數名,是無返回值類型無參數的函數了。
那麼很多人也許就會想,那我們何必還要聲明這一步呢?我們只要把所有的函數的定義都放在前面不就可以了嗎?這種想法是不可取的,一個好的程序員總是在程序的開頭聲明所有用到的函數和變量,這是為了以後好檢查。
前面說了,在調用之前,必須先聲明函數,所以下面的做法也是正確的(但在這裡我個人並不提倡)。
main()
{
void a();
a();
}
void a()
{
int num;
scanf(%d,&num);
printf(%d\n,num);
}
一般來說,比較好的程序書寫順序是,先聲明函數,然後寫主函數,然後再寫那些自定義的函數。
既然main()函數可以調用別的函數,那麼我們自己定義的函數能不能再調用其他函數呢?答案是可以的。看下面的例子:
void a();
void b();
main()
{
a();
}
void a()
{
b();
}
void b()
{
int num;
scanf(%d,&num);
printf(%d\n,num);
}
main()函數先調用a()函數,而a()函數又調用b()函數。在C語言裡,對調用函數的層數沒有嚴格的限制,我們可以往下調用100層、1000層,但是在這裡我們並不提倡調用的層數太多(除非是遞歸),因為層數太多,對以後的檢查有一些干擾,函數調過來調過去,容易讓自己都暈頭轉向。
某些人可能就不明白了,看上面的例子,好象使用函數後,程序變的更長了,更不讓人理解。當然,我舉的這個例子的確沒有必要用函數來實現,但是對於某些實際問題,如果不使用函數,會讓程序變的很亂,這涉及到參數問題,我們下一節再說。