本系統實現一個簡易的**姓名-號碼查詢管理系統**,採用 C 語言核心特性:
struct student {
char name[7]; // 姓名,最多 6 字元 + '\0'
int number; // 號碼(整數)
} student, student1; // 宣告兩個全域變數:寫入用、讀取用
設計考量:
void menu() {
printf("***********************\n");
printf("1 input record\n");
printf("2 search record\n");
printf("0 quit system\n");
}
int main() {
int select;
menu();
printf("\nPlease enter your select\n");
scanf("%d", &select);
while (select) { // 非 0 繼續
switch (select) {
case 1: save(); break;
case 2: search(); break;
case 0: exit(1); // 實際不會走到這,while 條件已攔截
}
// ⚠️ 缺陷:執行完 save()/search() 後不會重新顯示選單與讀取 select
// 會陷入無限迴圈或直接退出
}
}
關鍵 Bug 修正: `save()` 和 `search()` 內部呼叫了 `exit(0)`/`exit(1)`,導致程式直接結束,無法返回主選單。正確架構應移除函數內的 `exit()`,改回主迴圈重新讀取選項:
int main() {
int select;
while (1) {
menu();
printf("\nPlease enter your select: ");
scanf("%d", &select);
switch (select) {
case 1: save(); break;
case 2: search(); break;
case 0: printf("Goodbye!\n"); return 0;
default: printf("Invalid option!\n");
}
}
}
void save() {
FILE *fp = fopen("filename", "a+b"); // 二進制附加模式
if (!fp) { perror("open file error"); return; }
while (1) {
printf("Enter name and number: ");
scanf("%6s %d", student.name, &student.number); // %6s 防止緩衝區溢位
fwrite(&student, sizeof(struct student), 1, fp);
printf("Continue? (y/n): ");
char ch; scanf(" %c", &ch); // 空白吃掉殘留換行
if (ch == 'n' || ch == 'N') break;
}
fclose(fp);
}
改進點:
void search() {
FILE *fp = fopen("filename", "rb");
if (!fp) { perror("open file error"); return; }
char target[7];
printf("Enter name to search: ");
scanf("%6s", target);
int found = 0;
while (fread(&student1, sizeof(struct student), 1, fp) == 1) {
if (strcmp(student1.name, target) == 0) {
printf("Found: %s %d\n", student1.name, student1.number);
found = 1;
break;
}
}
if (!found) printf("Record not found.\n");
fclose(fp);
}
原代碼嚴重邏輯錯誤修正:
// 原代碼錯誤:
while (fread(...) == 0) { // 條件反了!fread 成功返回 1,失敗/結束返回 0
if (strcmp(...) == 0) ...
}
// 導致:檔案為空或讀完才進入迴圈,且漏讀第一筆記錄
正確邏輯:`while (fread(...) == 1)` 逐筆讀取並比對。
檔案 `filename` 內容為連續的定長記錄:
[Record 1: 7 bytes name][4 bytes number][Record 2: 7 bytes name][4 bytes number]...
實際記憶體佈局(小端序):
'J' 'o' 'h' 'n' '\0' '\0' '\0' | 0x39 0x05 0x00 0x00 (號碼 1337)
'A' 'l' 'i' 'c' 'e' '\0' '\0' | 0x78 0x00 0x00 0x00 (號碼 120)
優點: 讀寫極快(記憶體直接映射)、程式碼簡潔。
缺點: 不可跨平台(結構體對齊、端序、int 大小可能不同)、不支援可變長度字串、難以人工閱讀/編輯。
| 方向 | 技術點 | 實現提示 |
|---|---|---|
| 資料結構升級 | 動態陣列/鏈表 | 記憶體中維護鏈表,退出時序列化存檔 |
| 檔案格式現代化 | JSON / SQLite | 引入 cJSON 庫或 SQLite3,支援查詢、修改、刪除 |
| 查詢效能優化 | 索引 / 雜湊表 | 姓名建立雜湊索引,O(1) 定位記錄位移 |
| 介面增強 | ncurses / GUI | 文字介面選單、鍵盤導航、分頁顯示 |
| 健壯性加強 | 輸入驗證、錯誤恢復 | 檔案鎖定、異常中斷恢復、重複姓名處理 |
總結: 這個小系統雖簡陋(含多處 Bug),卻完整串起了 **C 語言檔案操作、結構體、選單迴圈、字串處理** 等核心知識點。對初學者極具價值:修復 Bug 的過程正是深入理解 `fread` 返回值、`scanf` 緩衝區、二進制 vs 文字模式、流程控制邏輯的最佳實踐。建議以此為基礎,逐步重構為健壯、可擴展的聯繫人管理系統。