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

菜鳥修煉C語言:通訊錄實戰進階——文件持久化與鏈表操作

     編輯:關於C

專案概述

本篇承接《通訊錄(一)》的基礎架構,重點新增聯系人資料保存功能,實現程式重啟後資料不丟失。專案採用單向鏈表動態管理記憶體,並透過檔案流(File I/O)完成資料的持久化存取,是 C 語言初學者鞏固指標、結構體與檔案操作的絕佳實戰案例。

核心知識點回顧

  • 檔案流操作:`fopen`、`fprintf`、`fscanf`、`fclose`、`rewind` 等標準庫函式的實戰應用。
  • 動態記憶體管理:使用 `malloc`/`free` 配合巨集 `ASK` 安全分配/釋放節點。
  • 鏈表增刪查:尾插法新增、按姓名刪除、遍歷顯示。
  • 多檔案模組化:`main.c`(主流程)、`record.h`(資料結構與巨集)、`opre.c`(業務邏輯)分離編譯。

關鍵程式碼解析

1. 資料結構定義 (record.h)

#ifndef _RECORD_H_
#define _RECORD_H_

typedef struct {
    char name[8];
    char tel[20];
} DATA;

typedef struct node {
    DATA data;
    struct node *next;
} ADDR;

// 安全分配巨集:失敗即終止程式
#define ASK(p) do { \
    p = (ADDR *)malloc(sizeof(ADDR)); \
    if (p == NULL) { \
        printf("malloc memory failed!"); \
        exit(-1); \
    } \
} while(0)

#endif

設計亮點:`ASK` 巨集封裝了記憶體分配與錯誤檢查,避免重複程式碼;`DATA` 結構體固定長度,適合二進位或格式化文字檔存儲。

2. 主程式流程控制 (main.c)

int main(int argc, char *argv[]) {
    int cmd = 0, flag = 1;
    while (1) {
        cmd = menu_select();
        if (cmd == '0') return 0;
        hand_menu(cmd, &flag); // 傳遞 flag 指標,確保 init_person 僅執行一次
    }
}

主迴圈極簡,所有業務邏輯委派給 `hand_menu`,透過 `static ADDR *list_head` 保持鏈表頭指針在函式呼叫間存活。

3. 檔案存取核心:存檔與讀檔 (opre.c)

存檔 `save_person`

void save_person(ADDR *list_head) {
    FILE *pf = fopen(FILE_NAME, "w+");
    ADDR *node = list_head;
    while (node) {
        fprintf(pf, "%s %s\n", node->data.name, node->data.tel);
        node = node->next;
    }
    fclose(pf);
    printf("保存成功!\n");
}

以 `w+` 模式開啟(建立新檔或截斷舊檔),逐節點寫入姓名與電話,格式化輸出便於後續 `fscanf` 讀取。

讀檔初始化 `init_person`

ADDR *init_person(ADDR *list_head) {
    FILE *pf = fopen(FILE_NAME, "r");
    if (!pf) return list_head; // 檔案不存在視為空通訊錄

    char name[8], tel[20];
    ADDR *head = NULL, *tail = NULL, *new_node;

    // 讀取第一筆判斷檔案是否為空
    if (fscanf(pf, "%s%s", name, tel) != 2) {
        fclose(pf);
        return head;
    }
    rewind(pf); // 重置檔案指標至開頭

    while (fscanf(pf, "%s%s", name, tel) == 2) {
        ASK(new_node);
        strcpy(new_node->data.name, name);
        strcpy(new_node->data.tel, tel);
        new_node->next = NULL;

        if (!head) head = tail = new_node;
        else {
            tail->next = new_node;
            tail = new_node;
        }
    }
    fclose(pf);
    return head;
}

關鍵細節:

  1. 先嘗試讀一筆,若失敗直接回傳空鏈表,避免建立無效節點。
  2. `rewind(pf)` 將指標歸零,確保後續迴圈完整讀取所有資料。
  3. 維護 `tail` 指標實現 O(1) 尾插,避免每次插入都遍歷到尾部。

常見問題與改進方向

問題風險建議改進
`scanf("%s", ...)` 無長度限制緩衝區溢位改用 `scanf("%7s", name)`、`scanf("%19s", tel)`
`getch()` 非標準函式跨平台移植困難改用 `getchar()` 搭配緩衝區清理,或條件編譯
檔案開啟未檢查 `NULL`權限不足或磁碟滿時崩潰所有 `fopen` 後必須判斷回傳值
姓名長度僅 7 字元(含結束符)中文姓名易截斷擴大 `name` 欄位至 32 位元組以上

編譯與執行

gcc main.c opre.c -o address_book
./address_book

執行後會在當前目錄產生 `phonebook.dat`,下次啟動自動載入歷史聯系人。

總結

此版本通訊錄完整串聯了動態資料結構與檔案持久化兩大 C 語言核心技能。透過模組化設計,程式碼結構清晰、易於維護擴展。建議後續嘗試加入查詢修改、二進位檔存儲、鏈表排序等功能,進一步深化對指標與記憶體模型的理解。

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