### 函數的定義
C語言程序中可以用分組的方式管理語句。
每個語句分組叫做一個函數。
多函數程序執行模式的特點:
* 整個程序的執行時間被分解成幾段,每段時間被分給一個函數使用。
* 任何兩個時間段不能互相重疊,并且所有時間段必須連續。
* 如果函數A在工作過程中把一段時間分配給函數B使用,則函數B在使用完所有時間之后必須把后面的時候還給函數A。
函數調用關系是有時間范圍的,只有被調用函數工作期間函數調用關系才存在。
在程序中使用函數調用語句產生調用關系。
C語言中不可以跨函數使用變量。
不同函數里的變量是可以重名的。
如果一個函數多次運行,則每次運行的時候內部變量對應的存儲區有可能不同。
volatile關鍵字可以用來聲明變量,使用這個關鍵字聲明的變量每次拿數的時候都可以得到存儲區里真正的數字。
函數調用過程中通常伴隨著兩個函數之間的數據傳遞。
數據傳遞存在兩個完全不同的方向,有可能從調用函數向被調用函數傳遞,也有可能從被調用函數向調用函數傳遞。
任何一個方向的數據傳遞都需要使用被調用函數提供的存儲區。
/*
函數演示
*/
#include <stdio.h>
void print(void) {
printf("1\n");
}
int main() {
print();
return 0;
}
### 函數的返回值
只能從被調用函數向調用函數傳遞一個數據,并且只能在被調用函數結束的時候才能傳遞這個數據,這個數據叫被調用函數的返回值。
函數的返回值必須記錄在被調用函數提供的一個專門的存儲區里,應該把這個存儲區的類型名稱寫在函數名稱前。
/*
函數返回值演示
*/
#include <stdio.h>
int read(void) {
int num = 0;
printf("請輸入一個數字:");
scanf("%d", &num);
return num;
}
int main() {
printf("read() = %d\n", read());
return 0;
}
調用函數直接把函數調用語句當成數字使用就可以得到被調用函數的返回值。
如果被調用函數里沒有使用`return`關鍵字指定返回值,則存放返回值的存儲區里的內容是隨機的,并且這個存儲區不能用來長期存放數字,調用函數得到返回值以后或者立刻使用或者轉存到其它存儲區里。
函數名稱前些void表示函數沒有提供用來存放返回值的存儲區。
函數名稱前不能什么都不寫。
被調用函數不能使用數組存放返回值。
### 函數的參數
調用函數可以一次向被調用函數傳遞多個不同類型的數據,被調用函數需要提供多個存儲區存放這些數據。
**形式參數**
可以用一組變量表示這些存儲區,這組變量必須聲明在函數名稱后面的小括號里,這些變量叫做形式參數,小括號里的內容統稱為形式參數列表。
形式參數列表是用逗號連接的多個變量的聲明語句,每個形式參數的類型名稱都不可以省略。
**實際參數**
函數調用語句里需要為每個形式參數提供一個對應的數據,這些數據叫實際參數。
函數調用的時候計算機把實際參數記錄到對應的形式參數里,被調用函數可以從形式參數里獲得實際參數的數值。
/*
函數演示
*/
#include <stdio.h>
void print(int num, int num1) {
printf("%d %d\n", num, num1);
}
int main() {
int num = 0, num1 = 0;
printf("請輸入兩個整數:");
scanf("%d%d", &num, &num1);
print(num, num1);
return 0;
}
所有能當數字使用的內容,都可以當做實際參數使用。
函數名稱后面的小括號里寫void表示函數不需要從調用函數得到任何數字,就不會給參數分配存儲區。
如果小括號里什么都沒寫表示函數可以提供任意多個任意類型的形式參數。
**數組形式參數**
數組可以作為形式參數使用。
/*
數組形式參數演示
*/
#include <stdio.h>
void print(int arr[]) {
int num = 0;
for (num = 0;num <= 4;num++){
printf("%d ", arr[num]);
}
printf("\n");
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
print(arr);
return 0;
}
數組做形式參數的時候只是把形式參數寫成數組的樣子,但真正的形式參數不是數組。
數組形式參數包含的所有存儲區都不是被調用函數提供的。
數組形式參數聲明的時候可以省略中括號里面的數字。
使用數組做形式參數的時候必須使用一個整數類型參數表示數組里的存儲區個數。
/*
數組形式參數演示
*/
#include <stdio.h>
void print(int arr[], int size) {
int num = 0;
for (num = 0;num <= size - 1;num++){
printf("%d ", arr[num]);
}
printf("\n");
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
print(arr, 5);
return 0;
}
使用數組作為形式參數可以讓被調用函數使用其他函數提供的存儲區。
**輸入輸出參數**
可以利用數組形式參數實現雙向數據傳遞,這種參數叫輸入輸出參數。
**變長參數**
C語言里允許函數的參數個數不固定,這種參數叫變長參數。
變長參數不能再編寫函數的時候命名,被調用函數里需要使用特殊方法才能獲得這些參數里的內容。
如果編譯器在編譯的時候首先遇到函數調用語句就會猜測函數的格式,這個猜測結果里函數用一個整數類型的存儲區記錄返回值,函數可以有任意多個不確定類型的形式參數。
### 函數的聲明
**隱式聲明**
這個猜測的結果叫函數的隱式聲明。
隱式聲明里函數的參數類型只能是整數類型或者雙精度浮點類型。
如果隱式聲明和函數的實際格式不一致,則會編譯失敗。
**顯示聲明**
函數大括號前面的部分可以單獨協程一條語句,這種語句叫函數聲明語句。
函數聲明語句里可以省略形式參數名稱。
把函數聲明語句單獨寫在文件的開頭叫做函數的顯示聲明。
顯示聲明可以避免隱式聲明出現的問題。
除了main主函數,其他的函數都應該使用顯示聲明的方式,聲明函數。
/*
顯示聲明演示
*/
#include <stdio.h>
double add(double, double);
int main() {
int num = add(5, 8);
printf("num = %d\n", num);
return 0;
}
double add(double num, double num1) {
return num + num1;
}
### 遞歸函數
C語言里允許自己調用自己。
這種可以調用自己的函數叫做遞歸函數。
如果一個問題可以分解成多個小問題,至少一個小問題和原來的問題本質上一樣,只是稍微簡單一點,具有這種特征的問題就適合采用遞歸函數。
遞歸函數編寫步驟:
* 編寫語句解決分解后的每個小問題。
* 在函數的開頭編寫分支處理不可分解的情況。(這個分支必須保證函數可以結束)
遞歸函數例子:
/*
遞歸函數演示
*/
#include <stdio.h>
void print(int num) {
if (num == 1){
printf("1 ");
return ;
}
print(num - 1);
printf("%d ", num);
}
int main(){
print(10);
printf("\n");
return 0;
}
檢驗遞歸函數的時候首先用最簡單的參數測試,然后逐漸把參數變復雜,如果這個過程可以持續下去就說明函數是正確的。
采用遞歸函數解決問題的思路叫遞歸。
采用循環解決類似問題的思路叫遞推。