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

菜鳥修煉C語言:成績統計——指標陣列與動態記憶體實戰

     編輯:關於C

專案背景與學習目標

本專案旨在透過一個「班級各科平均成績統計」的實例,深度鍛鍊 C 語言中最核心也最易混淆的指標與陣列關係、指標陣列操作、動態記憶體分配與釋放。需求看似簡單:輸入 5 科成績與人數,輸出各科成績、總平均、最低分科目與分數,但實作要求展示三種不同記憶體管理策略,層層遞進。

需求與資料結構設計

輸入/輸出範例

輸入:數學75 物理80 外語83 政治85 體育86 人數30
輸出:
全班各科平均成績如下:
數學:75  物理:80  外語:83  政治:85  體育:86
人數:30  平均成績:81  最低分科目成績:75  最低分科目:數學

三層資料陣列設計

陣列名型別用途大小
`a``int*` (動態)5科成績 + 人數 + 平均 + 最低分8 個 `int`
`name``char*[6]`5科名稱 + "科目"(最低分科目名)6 個指標
`mean_low``char*[4]`"人數"、"平均成績"、"最低分科目成績"、"最低分科目"4 個指標

核心程式碼逐節解析

1. 主流程與記憶體佈局 (main)

int main() {
    int *a;                     // 數值陣列:動態分配
    char *name[SUB_NUM+1];      // 科目名指標陣列
    char *mean_low[3];          // 統計項標籤指標陣列

    a = (int*)malloc(8 * sizeof(int));
    memset(a, 0, 8 * sizeof(int)); // 初始化為 0

    init_name(name);        // 靜態字串常量賦值
    init_mean_low(mean_low); // 同上
    input_message(name, mean_low, a);  // 輸入成績、人數
    analysis_message(a, name);         // 計算平均、最低分
    print_message(name, mean_low, a);  // 格式化輸出
    recycle_memory(name, mean_low, a); // 釋放所有記憶體
    return 0;
}

關鍵點:

  • 數值陣列 `a` 採用動態分配,體驗 `malloc`/`free` 流程。
  • 字串陣列 `name`、`mean_low` 存放字串常量指標,無需分配記憶體(指向 `.rodata` 區)。
  • 流程清晰:初始化 → 輸入 → 計算 → 輸出 → 釋放,符合 RAII 思想雛形。

2. 靜態字串初始化

void init_name(char *name[]) {
    name[0] = "數學"; name[1] = "物理"; name[2] = "外語";
    name[3] = "政治"; name[4] = "體育"; name[5] = "科目";
}
void init_mean_low(char *mean_low[]) {
    mean_low[0] = "人數"; mean_low[1] = "平均成績";
    mean_low[2] = "最低分科目成績"; mean_low[3] = "最低分科目";
}

直接指向字串常量,**不可修改內容**,亦無需 `free`。若後續需動態修改字串,必須改為 `malloc` + `strcpy`。

3. 輸入驗證與指標算術 (input_message)

void input_message(char *name[], char *mean_low[], int *a) {
    for (int i = 0; i < SUB_NUM; i++) {
        printf("%s: ", name[i]);
        scanf("%d", a + i); // 指標算術:a+i 等價 &a[i]
        while (a[i] > 100 || a[i] < 0) {
            printf("輸入錯誤,重新輸入\n%s: ", name[i]);
            scanf("%d", a + i);
        }
    }
    printf("請輸入班級人數: ");
    scanf("%d", a + SUB_NUM); // 人數存入 a[5]
}

展示指標下標引用法:`a+i`、`*(a+i)`、`a[i]` 三者等價,靈活運用可簡化碼風。

4. 統計分析核心 (analysis_message)

void analysis_message(int *a, char *name[]) {
    int sum = 0, min = 200, min_idx = 0;
    for (int i = 0; i < SUB_NUM; i++) {
        sum += a[i];
        if (a[i] < min) { min = a[i]; min_idx = i; }
    }
    a[SUB_NUM+1] = sum / SUB_NUM; // 平均存入 a[6]
    a[SUB_NUM+2] = min;           // 最低分存入 a[7]
    name[SUB_NUM] = name[min_idx]; // 最低分科目名存入 name[5]
}

單趟掃描完成求和與找最小值,**時間複雜度 O(n)**,空間複雜度 O(1) 額外空間。

5. 格式化輸出 (print_message)

void print_message(char *name[], char *mean_low[], int *a) {
    printf("分析結果為:\n");
    for (int i = 0; i < SUB_NUM; i++)
        printf("%s:%d\n", name[i], a[i]);
    printf("人數:%d\n", a[SUB_NUM]);
    printf("平均成績:%d\n", a[SUB_NUM+1]);
    printf("最低科目成績:%d\n", a[SUB_NUM+2]);
    printf("最低科目:%s\n", name[SUB_NUM]);
}

6. 記憶體回收——隱藏的嚴重 Bug (recycle_memory)

void recycle_memory(char *name[], char *mean_low[], int *a) {
    free(a); a = NULL;
    for (int i = 0; i < SUB_NUM+1; i++) {
        free(name[i]); name[i] = NULL; // ❌ 錯誤:釋放字串常量指標!
    }
    for (int i = 0; i < 4; i++) {
        free(mean_low[i]); mean_low[i] = NULL; // ❌ 同樣錯誤
    }
}

⚠ 致命錯誤分析:`name[]` 與 `mean_low[]` 存放的是**字串常量指標**(指向唯讀區),**絕不能 `free`**!執行時會導致未定義行為(通常是崩潰)。
正確做法:僅 `free(a)`;指標陣列本身在棧上,離開 `main` 自動回收,無需手動釋放元素。

三種實作難度對照(依原文要求)

級別描述關鍵差異
1. 陣列實作全部用靜態/棧上陣列無 `malloc`,最簡單但固定大小
2. 動態分配數值本專案做法:`a` 用 `malloc`體驗堆上數值陣列生命週期管理
3. 全動態含字串連字串也 `malloc` + `strcpy`最複雜,需雙重指標 `char**` 管理

編譯與執行

gcc grade_stat.c -o grade_stat
./grade_stat

關鍵知識點總結卡

  • 指標陣列 vs 二維陣列:`char *name[6]` 是 6 個獨立指標,可指向不同長度字串;`char name[6][20]` 是連續記憶體塊,每列固定 20 字元。
  • 動態陣列參數傳遞:`int *a` 實為指標,函式內 `a[i]` 即可存取,無需二重指標。
  • 字串常量唯讀性:`char *p = "abc"` → `p` 可改指向,但 `*p` 不可改;`free(p)` 嚴禁。
  • 指標算術優雅性:`scanf("%d", a+i)` 比 `&a[i]` 更能體現指標本質。

改進版記憶體釋放函式

void recycle_memory_fixed(int *a) {
    free(a); // 僅釋放真正 malloc 的塊
    // name, mean_low 為棧上指標陣列,元素指向常量區,無需釋放
}

總結

成績統計專案以極精簡的邏輯,精準切中 C 語言初學者最痛的三個點:**指標陣列語法、動態記憶體流程、字串常量與堆記憶體的區別**。原始碼中的 `recycle_memory` 錯誤,反而是最好的反面教材——**只有真正理解記憶體佈局(代碼段/堆/棧),才能寫出正確的 `malloc`/`free` 配對**。建議動手修復該 Bug,並嘗試實作「級別 3」全動態版本,徹底掌握雙重指標與字串深拷貝。

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