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

C 語言單鏈表檔案存取完整實現

     編輯:關於C語言

單鏈表資料的檔案持久化:完整讀寫實現與關鍵細節

單鏈表是動態資料結構,節點在堆中非連續分佈。將其存入檔案並還原,需解決指針無法直接序列化的問題。本文提供完整的 C 語言實現,涵蓋節點定義、寫入遍歷、讀取重建鏈接關係、邊界條件處理。

資料結構定義


#include 
#include 
#include 

struct Address {
    int a;
    int b;
    char c;
    struct Address *next;
};

寫入檔案:順序遍歷序列化


void SaveToFile(struct Address *head, FILE *fp) {
    if (!head) return;
    struct Address *cur = head;
    do {
        // 關鍵:僅寫入數據域,next 指針不寫入 (或寫入 0)
        // 這裡為演示簡化直接寫入整個結構,實際建議分開寫入數據域
        fwrite(cur, sizeof(struct Address), 1, fp);
        cur = cur->next;
    } while (cur != NULL);
}

注意:直接 fwrite 整個結構會將當時的 next 指針值 (堆地址) 寫入檔案,讀取後指針無效。生產代碼應定義無指針的「磁碟節點結構」或分別寫入數據域與標記。

讀取重建:動態分配並串聯


int LoadFromFile(FILE *fp, struct Address **head) {
    int count = 0;
    struct Address *prev = NULL, *cur = NULL;

    *head = NULL;
    while (!feof(fp)) {
        cur = (struct Address *)malloc(sizeof(struct Address));
        if (!cur) { /* 錯誤處理 */ break; }
        memset(cur, 0, sizeof(struct Address));

        size_t read = fread(cur, sizeof(struct Address), 1, fp);
        if (read != 1) {
            free(cur);
            break; // 讀取結束或出錯
        }
        cur->next = NULL;

        if (*head == NULL) {
            *head = cur;
        } else {
            prev->next = cur;
        }
        prev = cur;
        count++;
    }
    return count;
}

完整測試主程式


int main() {
    struct Address *nodes[10] = {0};
    struct Address *head = NULL;
    FILE *fp = NULL;
    int i;

    // 1. 建立 10 個節點並鏈接
    for (i = 0; i < 10; i++) {
        nodes[i] = (struct Address *)malloc(sizeof(struct Address));
        memset(nodes[i], 0, sizeof(struct Address));
        nodes[i]->a = 65 + i;  // 'A'...
        nodes[i]->b = 65 + i;
        if (i < 9) nodes[i]->next = nodes[i+1];
    }
    head = nodes[0];

    // 2. 寫入檔案
    fp = fopen("addrbook.dat", "wb");
    if (fp) {
        SaveToFile(head, fp);
        fclose(fp);
    }

    // 3. 釋放原鏈表 (模擬程式重啟)
    for (i = 0; i < 10; i++) free(nodes[i]);

    // 4. 從檔案重建
    fp = fopen("addrbook.dat", "rb");
    if (fp) {
        LoadFromFile(fp, &head);
        fclose(fp);
    }

    // 5. 驗證重建結果
    struct Address *p = head;
    while (p) {
        printf("Node: a=%d, b=%d, c=%c\n", p->a, p->b, p->c);
        struct Address *tmp = p;
        p = p->next;
        free(tmp); // 釋放重建的鏈表
    }
    return 0;
}

改進版:分離數據與指針 (推薦生產用法)


// 磁碟節點結構:無指針,固定大小
#pragma pack(push, 1)
struct DiskNode {
    int a;
    int b;
    char c;
    // 可加入 checksum/version
};
#pragma pack(pop)

void SaveToFileV2(struct Address *head, FILE *fp) {
    struct DiskNode dn;
    for (struct Address *p = head; p; p = p->next) {
        dn.a = p->a; dn.b = p->b; dn.c = p->c;
        fwrite(&dn, sizeof(dn), 1, fp);
    }
}

int LoadFromFileV2(FILE *fp, struct Address **head) {
    struct DiskNode dn;
    struct Address *prev = NULL, *cur = NULL;
    int count = 0;
    *head = NULL;
    while (fread(&dn, sizeof(dn), 1, fp) == 1) {
        cur = (struct Address *)malloc(sizeof(struct Address));
        cur->a = dn.a; cur->b = dn.b; cur->c = dn.c; cur->next = NULL;
        if (!*head) *head = cur;
        else prev->next = cur;
        prev = cur; count++;
    }
    return count;
}

關鍵點總結

  1. 指針不持久化:檔案中不能保存記憶體地址,讀取時必須重新 malloc 並手動串聯 next
  2. 結構體對齊陷阱:不同編譯器/平台對齊規則不同,跨平台檔案格式應使用 #pragma pack(1) 或逐字段讀寫
  3. 檔案結尾判斷:feof(fp) 在讀取失敗後才置位,迴圈條件應檢查 fread 返回值
  4. 錯誤處理:malloc 失敗、fread 短讀、檔案損壞皆需處理並釋放已分配節點
  5. 版本擴展:檔案頭加入魔數、版本號、節點數量,便於未來格式升級

文字模式替代方案

若資料為純文本可讀,亦可使用 fprintf/fscanf 或 fgets/sscanf 逐行處理,避免二進制對齊問題,但儲存空間較大、解析較慢。

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