基於單向鏈表實現的控制台通訊錄系統,功能涵蓋:增刪改查、分類篩選、自動排序、撥號模擬、持久化存儲。代碼採用 C++ 風格 (引用、new/delete、iostream) 但邏輯純 C 風格,適合 C 語言學習者參考數據結構落地。
typedef struct {
char name[25]; // 姓名
char number[50]; // 電話
int id; // 序號 (自動維護)
char category; // 分類 A/B/C
char email[50]; // 郵箱
} Contact;
typedef struct Node {
Contact data;
struct Node *next;
} Node, *LinkList; // LinkList 為指向頭節點的指針
LinkList init_list() {
LinkList L = (LinkList)malloc(sizeof(Node)); // 頭節點不存數據
L->next = NULL;
return L;
}
void free_list(LinkList L) {
Node *p = L->next, *tmp;
while (p) { tmp = p; p = p->next; free(tmp); }
free(L);
}
int add_contact(LinkList L) {
if (count_contacts(L) >= 15) { puts("通訊錄已滿"); return 0; }
Node *p = (Node*)malloc(sizeof(Node));
p->next = NULL;
printf("姓名: "); scanf("%24s", p->data.name);
printf("電話: "); scanf("%49s", p->data.number);
// 去重檢查
if (find_by_name_or_number(L, p->data.name, p->data.number)) {
puts("聯系人已存在"); free(p); return 0;
}
printf("分類(A/B/C): "); scanf(" %c", &p->data.category);
printf("郵箱: "); scanf("%49s", p->data.email);
// 尾插
Node *tail = L;
while (tail->next) tail = tail->next;
tail->next = p;
renumber(L); // 重新分配序號
return 1;
}
int delete_contact(LinkList L) {
char name[25];
printf("刪除姓名: "); scanf("%24s", name);
Node *prev = L, *cur = L->next;
while (cur && strcmp(cur->data.name, name) != 0) {
prev = cur; cur = cur->next;
}
if (!cur) { puts("未找到"); return 0; }
prev->next = cur->next;
free(cur);
renumber(L);
puts("刪除成功"); return 1;
}
category 輸出strcmp 匹配
void simulate_dial(const char *number) {
system("cls");
for (int i = 0; number[i]; ++i) {
putchar('\a'); // 蜂鳴器
putchar(number[i]); // 逐字符輸出
fflush(stdout);
Sleep(300); // windows.h, 300ms 間隔
}
putchar('\n');
}
void save_to_file(LinkList L, const char *filename) {
FILE *fp = fopen(filename, "w");
for (Node *p = L->next; p; p = p->next)
fprintf(fp, "%s,%s,%c,%s\n", p->data.name, p->data.number, p->data.category, p->data.email);
fclose(fp);
}
void load_from_file(LinkList L, const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) return;
char line[200];
while (fgets(line, sizeof(line), fp)) {
Node *p = (Node*)malloc(sizeof(Node));
p->next = NULL;
sscanf(line, "%24[^,],%49[^,],%c,%49[^\n]", p->data.name, p->data.number, &p->data.category, p->data.email);
// 尾插...
}
fclose(fp);
renumber(L);
}
int main(void) {
LinkList L = init_list();
load_from_file(L, "contacts.csv");
int choice;
while (1) {
print_menu();
scanf("%d", &choice);
switch (choice) {
case 1: list_all(L); break;
case 2: query_by_category(L); break;
case 3: delete_contact(L); break;
case 4: add_contact(L); break;
case 5: simulate_dial_by_name(L); break;
case 0: save_to_file(L, "contacts.csv"); free_list(L); return 0;
default: puts("無效選項");
}
}
}
| 問題 | 原代碼 | 改進方案 |
|---|---|---|
| 輸入緩衝區溢出 | scanf("%s", name) | scanf("%24s", name) 限制寬度 |
| 混合 C/C++ 風格 | new/delete, iostream, malloc/free 混用 | 純 C: malloc/free, stdio.h; 或純 C++: new/delete, iostream, string, vector |
fflush(stdin) 未定義行為 | 清理輸入緩衝區 | int c; while((c=getchar())!='\n' && c!=EOF); |
| 無錯誤碼返回 | 關鍵函數無返回值 | 關鍵操作返回 int (0 失敗/1 成功),主循環檢查 |
| 硬編碼限制 | 最大 15 聯系人 | 配置常量 MAX_CONTACTS,動態分配無上限 |
| 跨平台兼容 | Sleep, system("cls"), \\a | 抽象平台層,或用 ANSI 轉義序列 \033[2J\033[H 清屏 |
這個通訊錄項目雖屬教學級,但完整覆蓋了 動態內存管理、鏈表操作、字符串處理、文件 I/O、模塊化設計、用戶交互、平台 API 調用 等 C 語言核心工程技能。建議學習者:
這就是從「跟著敲代碼」到「獨立交付項目」的關鍵躍遷。