將特定功能或常用的程式碼獨立成一個程式模組,稱為函式。
函式的優點為:
函式分為兩種:公用函式以及自訂函式。
常見的標頭檔如下,只要在程式中引入(#include<標頭檔>
),即可呼叫公用函式:
常見的數值類
公用函式如下表:
角度A → 弧度:A×π/180
時間、日期類函式如下表:
使用範例:
#include <stdio.h>
#include <time.h>
int main() {
time_t t = time(&t);
printf("現在時間:%s\n", ctime(&t));
int j;
printf("執行加法運算5億次需要");
clock_t st =clock();
for(int i=0; i<500000000; i++)
{
j+=1;
}
clock_t et =clock();
printf("%.3lf秒",(double)(et-st)/CLOCKS_PER_SEC);
return 0;
}
st
)。et
)。CLOCKS_PER_SEC
(代表CPU時鐘的每秒執行次數),即可得到所需的秒數。double
)先強制轉換資料型態。亂數函式如下:
使用範例:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
srand(time(NULL)); // 設定亂數種子,如果沒有設定,會造成下一次程式執行的輸出結果與前一次相同
for (int i = 0; i < 5; i++) {
printf("亂數%d: %d\n", i + 1, rand() % 100); // 產生 0-99 的亂數
}
for (int i = 0; i < 5; i++) {
printf("亂數%d: %d\n", i + 1, rand() % 100 + 1); // 產生 1-100 的亂數
}
return 0;
}
自訂函式是由開發者自己定義的函式,用於執行特定需求。它可以提升程式的可讀性、模組化和重複使用性。
函式定義:
返回值型別 函式名稱(參數型別 參數名稱, ...) {
// 函式內部的程式碼
return 返回值; // (如果函式需要回傳值)
}
若不需要傳入參數以及回傳變數值,則函式宣告如下:
void 函式名稱(參數型別 參數名稱, ...) {
// 函式內部的程式碼
}
簡單的自訂函式例子:
#include <stdio.h>
// 函式宣告
int add(int a, int b);
int main() {
int x = 5, y = 10;
// 呼叫函式
int result = add(x, y); //result用來接收x+y的結果
printf("兩數的總和為:%d\n", result);
return 0;
}
// 函式定義
int add(int a, int b) {
return a + b; // 傳回兩數的和,傳回int型別
}
當然,你也可以宣告後直接定義:
#include <stdio.h>
// 函式宣告並定義
int add(int a, int b){
return a + b; // 傳回兩數的和
}
int main() {
int x = 5, y = 10;
// 呼叫函式
int result = add(x, y);
printf("兩數的總和為:%d\n", result);
return 0;
}
請注意,當我們呼叫函式並傳入數值時,函式接收的是變數值的副本,而不是原變數本身。這種方式不會影響原始變數的內容。以下為簡單示範:
#include <stdio.h>
// 函式宣告
void modifyValue(int a);
int main() {
int a = 10;
printf("呼叫函式前,a = %d\n", a);
// 呼叫函式,傳遞 x 的值
modifyValue(a);
printf("呼叫函式後,a = %d\n", a);
return 0;
}
// 函式定義
void modifyValue(int a) {
a = 20; // 修改參數的副本
printf("函式內,a = %d\n", a);
}
從上面的程式碼,我們可以學到,函式中的a
是你傳入數值的一個副本(傳入的數值複製給函式中的a
變數),並且這個副本變數允許和主程式中的變數a
同名,不過他們本質上並不一樣,所以修改函式中的a
並不會影響主程式中的變數a
。
接下來我想要講解變數的生命週期:
#include <stdio.h>
// 全域變數
int globalVar = 100; // 生命週期:程式開始 → 程式結束
void testFunction() {
// 區域變數
int localVar = 10; // 生命週期:進入函式 → 函式結束
printf("函式內區域變數 localVar = %d\n", localVar);
// 靜態區域變數
static int staticVar = 0; // 生命週期:第一次進入函式 → 程式結束
staticVar++;
printf("函式內靜態區域變數 staticVar = %d\n", staticVar);
}
int main() {
// main 函式內的區域變數
int mainVar = 50; // 生命週期:進入 main() → main() 結束
printf("main 函式內區域變數 mainVar = %d\n", mainVar);
// 迴圈內的區域變數
for (int i = 0; i < 3; i++) {
int loopVar = i * 10; // 生命週期:每次進入迴圈 → 該次迴圈結束
printf("迴圈內區域變數 loopVar = %d\n", loopVar);
}
// 呼叫函式多次
testFunction(); // 第一次呼叫
testFunction(); // 第二次呼叫
// 使用全域變數
printf("全域變數 globalVar = %d\n", globalVar);
return 0;
}
變數生命週期與範圍解釋:
globalVar
):mainVar
):main()
時分配記憶體,退出 main()
時釋放。main()
。localVar
):staticVar
):loopVar
):讀者目前應該瞭解了上述的概念。
前面有提到,單純傳數值進去(傳值),並不會影響原本的數值,哪有沒有甚麼方法是能夠直接修改到原本傳進來的變數呢?
這個方法叫做"傳址函式",不傳數值而改成傳入變數的位址,如此,在函式內就能直接對傳入的變數進行存取與修改。
下面我們示範一個數值交換函式:
#include <stdio.h>
// 函式宣告
void swap(int *a, int *b);
int main() {
int x = 5, y = 10;
printf("交換前:x = %d, y = %d\n", x, y);
// 傳址呼叫
swap(&x, &y);
printf("交換後:x = %d, y = %d\n", x, y);
return 0;
}
// 函式定義
void swap(int *a, int *b) {
int temp = *a; // 使用指標解參考,取得 a 指向的值
*a = *b; // 修改 a 指向的值
*b = temp; // 修改 b 指向的值
}
如此,就達到了直接修改傳入變數內容的目的了。(關於指標的觀念可以去觀看指標的那一章節)。
好,接著我必須再介紹一個不得不知道的函式觀念:遞迴。
遞迴是甚麼呢? 遞迴是函式在執行過程中呼叫函式本身的一種技術。遞迴可以用來解決問題,特別是那些可以分解成較小規模的同類型問題的情況。 使用遞迴有兩個條件:
返回值型別 函式名稱(參數) {
if (結束條件) {
return 結果; // 遞迴結束條件
} else {
遞迴呼叫; // 繼續呼叫自身
}
}
簡單觀念如下:
今天我們想要計算階乘,例如5!等於5X4X3X2X1,我們可以有以下的想法:
factorial(5) = 5 * factorial(4)
factorial(4) = 4 * factorial(3) //上面的factorial(4) = 4 * factorial(3),接著以此類推
factorial(3) = 3 * factorial(2)
factorial(2) = 2 * factorial(1)
factorial(1) = 1 (結束條件)
最後我們依序把值帶回,求出數值
factorial(2) = 2 * 1 = 2
factorial(3) = 3 * 2 = 6
factorial(4) = 4 * 6 = 24
factorial(5) = 5 * 24 = 120 //最後得到答案的步驟
5X(4X(3X(2X(1))))
= 1X2X3X4X5 = 120程式範例:
#include <stdio.h>
// 遞迴函式定義
int factorial(int n) {
if (n == 0 || n == 1) {
return 1; // 結束條件:也要考慮0!得情況 : 0! = 1! = 1
} else {
return n * factorial(n - 1); // 繼續遞迴:n! = n * factorial(n-1)!
}
}
int main() {
int num = 5;
printf("%d 的階乘是:%d\n", num, factorial(num)); // 呼叫遞迴函式
return 0;
}