單鏈表是動態資料結構,節點在堆中非連續分佈。將其存入檔案並還原,需解決指針無法直接序列化的問題。本文提供完整的 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;
}
malloc 並手動串聯 next#pragma pack(1) 或逐字段讀寫feof(fp) 在讀取失敗後才置位,迴圈條件應檢查 fread 返回值malloc 失敗、fread 短讀、檔案損壞皆需處理並釋放已分配節點若資料為純文本可讀,亦可使用 fprintf/fscanf 或 fgets/sscanf 逐行處理,避免二進制對齊問題,但儲存空間較大、解析較慢。