現實世界的軟體專案往往龐大複雜,單一的主函數無法承載所有邏輯。C語言採用「自頂向下、逐步求精」的模組化設計思想:將大問題分解為獨立的小功能模組,每個模組獨立開發、測試,最後整合成完整程式。這不僅提高了程式碼的可讀性與維護性,也促進了團隊協作與程式碼複用。
這個問題可拆解為四個子模組:
#include <stdio.h>
/* (1) 判斷閏年:返回 1 表示閏年,0 表示平年 */
int leap(int year) {
int flag;
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
flag = 1;
else
flag = 0;
return flag;
}
/* (2) 計算某年某月的天數 */
int month_days(int year, int month) {
int d;
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
d = 31; break;
case 2:
d = leap(year) ? 29 : 28; /* 呼叫 leap() 判斷 */
break;
default:
d = 30; break;
}
return d;
}
/* (3) 累加天數:呼叫 month_days() */
int days(int year, int month, int day) {
int i, ds = 0;
for (i = 1; i < month; i++)
ds += month_days(year, i);
ds += day;
return ds;
}
/* 主程式:整合各模組 */
int main(void) {
int year, month, day, total_day;
printf("請輸入日期 (年-月-日): ");
scanf("%d-%d-%d", &year, &month, &day);
total_day = days(year, month, day);
printf("%d-%02d-%02d 是該年的第 %d 天\n", year, month, day, total_day);
return 0;
}
上例展示了函數之間的層層呼叫:main() → days() → month_days() → leap(),結構清晰,各司其職。
printf, scanf)由系統提供,直接呼叫;使用者自訂函數需自行實作並宣告原型。int, float, 指標等);無返回值函數用 void 宣告,僅執行特定操作。回傳型別 函數名稱(型別 形參1, 型別 形參2, ...) {
/* 函數本體:具體實作邏輯 */
return 運算式; /* 有返回值時必須有 return */
}
注意:C99 標準建議使用「新式定義」(參數列表帶型別),舊式「K&R 風格」已不推薦。
#include <stdio.h>
#include <math.h>
/* 函數原型宣告 */
int prime(int n);
int main(void) {
int n, flag;
printf("請輸入一個整數: ");
scanf("%d", &n);
flag = prime(n); // 呼叫函數
if (flag)
printf("%d 是質數\n", n);
else
printf("%d 不是質數\n", n);
return 0;
}
/* 判斷質數:是則返回 1,否則 0 */
int prime(int n) {
int m;
for (m = 2; m <= sqrt(n); m++)
if (n % m == 0) return 0;
return 1;
}
變數 = 函數名(引數列表);函數名(引數列表); 作為獨立語句執行。當被呼叫函數定義在主呼叫函數之後,或位於不同原始檔時,必須在呼叫端前方提供原型宣告,告知編譯器返回型別與參數資訊,以便進行型別檢查與參數轉換。
long sum(int);。#include "fun.c" 或建立 header 檔 #include "fun.h" 並分別編譯連結。C語言採用值傳遞:實參的值複製給形參,形參在函數內擁有獨立記憶體空間,修改形參不影響實參。
void fun(int num) {
num = 20;
printf("fun 內部 num = %d\n", num); // 輸出 20
}
int main(void) {
int x = 10;
fun(x);
printf("main 內部 x = %d\n", x); // 輸出 10,未被改變
return 0;
}
C 標準未規定實參求值順序,不同編譯器可能不同。避免在實參中使用副作用運算(如 i++),否則行為不可預期。
return 運算式; 將值傳回呼叫端,同時釋放函數堆疊框架,控制權回到呼叫點。void 函數可用 return; 提前結束。
陣列名傳遞時退化為首元素位址,形參接收的是指標,因此函數內對陣列元素的修改直接影響原陣列(模擬引用傳遞)。
void input(int a[], int n) { // 等價於 int *a
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
}
int max_val(int a[], int n) {
int m = a[0];
for (int i = 1; i < n; i++) if (a[i] > m) m = a[i];
return m;
}
void print_arr(int a[], int n) {
for (int i = 0; i < n; i++) printf("%4d", a[i]);
putchar('\n');
}
int main(void) {
int b[10];
input(b, 10);
printf("最大值: %d\n", max_val(b, 10));
print_arr(b, 10);
return 0;
}
形參寫成 int a[10] 或 int *a 效果相同,編譯器均視為指標。
C 不允許函數定義巢狀,但允許呼叫巢狀。例如哥德巴赫猜想驗證:main() → even() → prime()。
遞迴必須包含基底情況(終止條件),否則無限遞迴導致堆疊溢位。
/* 遞迴求階乘 */
long factorial(int n) {
if (n <= 1) return 1; // 基底情況
return n * factorial(n-1); // 遞迴步驟
}
本文整理自 C 語言學習筆記系列,旨在系統梳理函數核心知識點,助力夯實程式設計基礎。