首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言核心筆記(九):函數詳解與模組化程式設計

     編輯:關於C語言

一、 程式模組化:化繁為簡的藝術

現實世界的軟體專案往往龐大複雜,單一的主函數無法承載所有邏輯。C語言採用「自頂向下、逐步求精」的模組化設計思想:將大問題分解為獨立的小功能模組,每個模組獨立開發、測試,最後整合成完整程式。這不僅提高了程式碼的可讀性與維護性,也促進了團隊協作與程式碼複用。

經典案例:計算「某年某月某日是當年的第幾天」

這個問題可拆解為四個子模組:

  1. 獲取輸入(年、月、日)
  2. 判斷閏年
  3. 計算指定月份的天數
  4. 累加前幾個月天數並加上當天日期

程式碼實現

#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(),結構清晰,各司其職。

二、 函數的定義與分類

2.1 三種分類視角

  • 依定義來源:庫函數(如 printf, scanf)由系統提供,直接呼叫;使用者自訂函數需自行實作並宣告原型。
  • 依返回值:有返回值函數需指定返回型別(int, float, 指標等);無返回值函數用 void 宣告,僅執行特定操作。
  • 依參數傳遞:無參函數不接收資料;有參函數包含形式參數(定義時的佔位符)與實際參數(呼叫時傳入的具體值)。

2.2 函數定義語法

回傳型別 函數名稱(型別 形參1, 型別 形參2, ...) {
    /* 函數本體:具體實作邏輯 */
    return 運算式;  /* 有返回值時必須有 return */
}

注意:C99 標準建議使用「新式定義」(參數列表帶型別),舊式「K&R 風格」已不推薦。

2.3 範例:判斷質數

#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;
}

三、 函數呼叫機制與原型宣告

3.1 呼叫形式

  • 有返回值:變數 = 函數名(引數列表);
  • 無返回值:函數名(引數列表); 作為獨立語句執行。

3.2 函數原型宣告的必要性

當被呼叫函數定義在主呼叫函數之後,或位於不同原始檔時,必須在呼叫端前方提供原型宣告,告知編譯器返回型別與參數資訊,以便進行型別檢查與參數轉換。

3.3 三種相對位置範例

  1. 被呼叫函數在 main 之後:需在 main 前宣告原型 long sum(int);。
  2. 被呼叫函數在 main 之前:定義本身即作為宣告,無需額外原型。
  3. 跨檔案呼叫:在主檔以 #include "fun.c" 或建立 header 檔 #include "fun.h" 並分別編譯連結。

四、 參數傳遞:值傳遞與指標模擬引用

4.1 值傳遞本質

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;
}

4.2 多參數求值順序

C 標準未規定實參求值順序,不同編譯器可能不同。避免在實參中使用副作用運算(如 i++),否則行為不可預期。

4.3 返回值機制

return 運算式; 將值傳回呼叫端,同時釋放函數堆疊框架,控制權回到呼叫點。void 函數可用 return; 提前結束。

五、 陣列作為函數參數:地址傳遞

陣列名傳遞時退化為首元素位址,形參接收的是指標,因此函數內對陣列元素的修改直接影響原陣列(模擬引用傳遞)。

5.1 一維陣列參數範例

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 效果相同,編譯器均視為指標。

六、 函數巢狀呼叫與遞迴

6.1 巢狀呼叫

C 不允許函數定義巢狀,但允許呼叫巢狀。例如哥德巴赫猜想驗證:main() → even() → prime()。

6.2 遞迴:函數呼叫自身

遞迴必須包含基底情況(終止條件),否則無限遞迴導致堆疊溢位。

/* 遞迴求階乘 */
long factorial(int n) {
    if (n <= 1) return 1;      // 基底情況
    return n * factorial(n-1); // 遞迴步驟
}

七、 練習建議

  1. 實作遞迴與迴圈兩種版本的階乘、費氏數列,比較效能。
  2. 撰寫函數找出四個數中的最大值。
  3. 挑戰:利用函數模組化印出完整年曆(需處理閏年、每月天數、起始星期)。

本文整理自 C 語言學習筆記系列,旨在系統梳理函數核心知識點,助力夯實程式設計基礎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved