基於 VC++ 6.0 開發的控制台電話號碼管理系統,支援新增、修改、刪除、查詢(姓名/電話)、顯示全部、文件清理等功能,數據持久化存儲於二進制文件 telefon.dat。
struct fileh { // 文件頭:記錄總條數
long fc;
} fh;
struct telsys { // 記錄結構:姓名、地址、電話
char usename[30];
char useadd[50];
long usetel;
} tel[200]; // 最大 200 條記錄
struct fileh(4/8 位元組),存儲當前記錄數 fc。fc 個 struct telsys 連續存儲,每筆固定大小。fseek 定位 + fread/fwrite 實現隨機存取。qingli()將內存中有效記錄重寫回文件,消除刪除殘留的「垃圾數據」,壓縮文件體積。
void qingli() {
FILE *f;
long i = 0;
// 1. 讀取所有有效記錄到內存 tel[]
f = fopen("telefon.dat", "rb");
fread(&fh, sizeof(fh), 1, f);
while (i < fh.fc) {
fseek(f, sizeof(fh) + i * sizeof(struct telsys), SEEK_SET);
fread(&tel[i], sizeof(struct telsys), 1, f);
i++;
}
fclose(f);
// 2. 以 wb+ 截斷重寫
f = fopen("telefon.dat", "wb+");
i = 0;
while (i < fh.fc) {
fseek(f, 0, SEEK_SET);
fwrite(&fh, sizeof(fh), 1, f);
fseek(f, sizeof(fh) + i * sizeof(struct telsys), SEEK_SET);
fwrite(&tel[i], sizeof(struct telsys), 1, f);
i++;
}
fclose(f);
}
addnew()fh.fc < 200 容量上限。fh.fc → 寫文件頭 → 寫新記錄。del()fh.fc-- → 重寫文件頭與後續記錄。qingli(),文件中仍殘留舊數據,佔用空間。findname() / findtel()tel[](fread(&tel, fh.fc*sizeof(...), 1, f))。edit()tel[in-1] 並 fseek 寫回對應文件位置。display()getch() 等待按鍵。void main() {
FILE *f = fopen("telefon.dat", "ab"); // 確保文件存在
fclose(f);
char ch;
do {
system("cls");
// 打印選單...
ch = getch();
switch (ch) {
case '1': addnew(); break;
case '2': edit(); break;
case '3': del(); break;
case '4': findtel(); break;
case '5': findname(); break;
case '6': display(); getch(); break;
case '7': quit(); break;
}
} while (ch != '7');
}
| 問題 | 風險 | 建議 |
|---|---|---|
gets/scanf("%s") 無長度限制 | 緩衝區溢出 | 使用 fgets 或 scanf("%29s") 限制長度 |
全域陣列 tel[200] 固定大小 | 擴展性差 | 動態分配 malloc/realloc 或鏈表 |
刪除不壓縮文件,退出才 qingli() | 文件膨脹、碎片化 | 刪除後即時重寫或引入標記刪除+定期整理 |
system("cls")、gotoxy 平台相關 | 不可移植 | 抽象界面層,使用 ncurses/Windows API |
| 無輸入錯誤恢復(如非數字輸入) | 程序進入死循環 | 檢查 scanf 返回值,清理輸入緩衝區 |
電話號碼用 long 存儲 | 不支援前導零、分機、國際格式 | 改用 char[20] 字符串存儲 |
本系統展示了 C 語言結構化程式設計、二進制文件隨機存取、控制台 UI 繪製的經典範式。雖代碼風格屬早期教學級(全域變量、固定陣列、平台依賴),但邏輯完整、功能齊全,是理解文件操作與菜單驅動程式架構的優秀案例。現代化重構可引入模塊化設計、動態內存、跨平台庫與輸入驗證機制。