本篇承接《通訊錄(一)》的基礎架構,重點新增聯系人資料保存功能,實現程式重啟後資料不丟失。專案採用單向鏈表動態管理記憶體,並透過檔案流(File I/O)完成資料的持久化存取,是 C 語言初學者鞏固指標、結構體與檔案操作的絕佳實戰案例。
#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` 結構體固定長度,適合二進位或格式化文字檔存儲。
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` 保持鏈表頭指針在函式呼叫間存活。
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` 讀取。
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;
}
關鍵細節:
| 問題 | 風險 | 建議改進 |
|---|---|---|
| `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 語言核心技能。透過模組化設計,程式碼結構清晰、易於維護擴展。建議後續嘗試加入查詢修改、二進位檔存儲、鏈表排序等功能,進一步深化對指標與記憶體模型的理解。