本專案實作一個**教師資訊管理系統**,功能涵蓋:新增、顯示、查詢、修改、刪除、檔案存取 (文字/二進位)、動態記憶體擴容。展示 C 語言結構體、指標、動態陣列、檔案操作、模組化設計的綜合應用。
#define SIZE sizeof(Teacher)
#define INIT_CAPACITY 10
typedef struct {
char number[20]; // 工號
char name[20]; // 姓名
char sex[10]; // 性別: "man"/"woman"
char addr[50]; // 地址
char married[10]; // 婚姻: "Yes"/"No"
} Teacher;
Teacher *tea = NULL; // 動態陣列基址
int numtea = 0; // 目前教師數
int lens = 0; // 目前配置容量
void add_tea(Teacher **tea) { // 傳二級指標,可修改主程式 tea 基址
char str[5];
int num, i = 0;
if (numtea != 0) {
printf("覆蓋現有記錄? (y/n): ");
fgets(str, sizeof(str), stdin);
if (str[0] != 'y' && str[0] != 'Y') i = numtea; // 續接模式
}
printf("輸入新增教師數: ");
scanf("%d", &num);
while (getchar() != '\n'); // 清緩衝區
if (i == 0) numtea = num; else numtea += num;
// 容量檢查與擴容 (realloc)
if (numtea > lens) {
lens += 10;
Teacher *tmp = realloc(*tea, lens * SIZE);
if (!tmp) { perror("記憶體不足"); exit(1); }
*tea = tmp; // 更新基址
}
for (; i < numtea; i++) {
printf("教師 %d 資訊:\n", i+1);
printf("工號: "); fgets((*tea)[i].number, sizeof((*tea)[i].number), stdin); (*tea)[i].number[strcspn((*tea)[i].number, "\n")] = 0;
printf("姓名: "); fgets((*tea)[i].name, sizeof((*tea)[i].name), stdin); (*tea)[i].name[strcspn((*tea)[i].name, "\n")] = 0;
printf("性別 (0=man/1=woman): "); fgets(str, sizeof(str), stdin);
strcpy((*tea)[i].sex, (str[0]=='0') ? "man" : "woman");
printf("地址: "); fgets((*tea)[i].addr, sizeof((*tea)[i].addr), stdin); (*tea)[i].addr[strcspn((*tea)[i].addr, "\n")] = 0;
printf("婚姻 (0=No/1=Yes): "); fgets(str, sizeof(str), stdin);
strcpy((*tea)[i].married, (str[0]=='0') ? "No" : "Yes");
}
display_tea(*tea, 0, numtea-1);
}
關鍵點:
Teacher **tea:realloc 可能移動記憶體,必須透過二級指標更新主程式指標realloc 安全模式:用暫存指標接收,檢查 NULL 再賦值,避免記憶體洩漏fgets + 去換行,比 gets (已移除 C11) / scanf 安全void display_tea(Teacher *tea, int n1, int n2) {
if (numtea == 0) {
puts("無資料");
return;
}
for (int i = n1; i <= n2; i++) {
printf("工號:%-10s 姓名:%-10s 性別:%-6s 地址:%-20s 婚姻:%s\n",
tea[i].number, tea[i].name, tea[i].sex, tea[i].addr, tea[i].married);
}
}
int find_tea(Teacher *tea) {
if (numtea == 0) return -1;
char str[20], field[10];
printf("查詢欄位 (1.工號 2.姓名 3.地址): ");
fgets(field, sizeof(field), stdin);
printf("輸入關鍵字: ");
fgets(str, sizeof(str), stdin);
str[strcspn(str, "\n")] = 0;
for (int i = 0; i < numtea; i++) {
int match = 0;
if (field[0]=='1') match = strcmp(tea[i].number, str) == 0;
else if (field[0]=='2') match = strcmp(tea[i].name, str) == 0;
else if (field[0]=='3') match = strcmp(tea[i].addr, str) == 0;
if (match) { display_tea(tea, i, i); return i; }
}
puts("查無資料");
return -1;
}
void adjust_tea(Teacher *tea) {
char str[5];
while (1) {
int idx = find_tea(tea);
if (idx < 0) return;
printf("刪除? (y/n): "); fgets(str, sizeof(str), stdin);
if (str[0]=='y' || str[0]=='Y') {
delete_tea(tea, idx);
continue;
}
printf("修改? (y/n): "); fgets(str, sizeof(str), stdin);
if (str[0]=='y' || str[0]=='Y') {
// 重新輸入該筆資料 (略,同 add_tea 邏輯)
}
printf("繼續調整? (y/n): "); fgets(str, sizeof(str), stdin);
if (str[0]!='y' && str[0]!='Y') break;
}
}
void delete_tea(Teacher *tea, int idx) {
for (int i = idx; i < numtea - 1; i++) tea[i] = tea[i+1]; // 結構體整體賦值移動
numtea--;
puts("刪除成功");
}
// 寫入文字檔 (可讀、可攜)
int write_txt(Teacher *tea) {
char filename[50];
printf("檔名: "); fgets(filename, sizeof(filename), stdin);
filename[strcspn(filename, "\n")] = 0;
FILE *fp = fopen(filename, "w");
if (!fp) { perror("開檔失敗"); return -1; }
fprintf(fp, "%d\n", numtea);
for (int i = 0; i < numtea; i++) {
fprintf(fp, "%s %s %s %s %s\n",
tea[i].number, tea[i].name, tea[i].sex, tea[i].addr, tea[i].married);
}
fclose(fp);
puts("儲存成功");
return 0;
}
// 讀取文字檔
int read_txt(Teacher **tea) {
char filename[50], str[5];
printf("檔名: "); fgets(filename, sizeof(filename), stdin);
filename[strcspn(filename, "\n")] = 0;
if (numtea != 0) {
puts("現有資料未存檔,先存檔? (y/n)");
fgets(str, sizeof(str), stdin);
if (str[0]=='y' || str[0]=='Y') write_txt(*tea);
}
FILE *fp = fopen(filename, "r");
if (!fp) { perror("開檔失敗"); return -1; }
int count;
fscanf(fp, "%d\n", &count);
if (count > lens) {
lens = count + 10;
Teacher *tmp = realloc(*tea, lens * SIZE);
if (!tmp) { fclose(fp); return -1; }
*tea = tmp;
}
for (int i = 0; i < count; i++) {
fscanf(fp, "%s %s %s %s %s\n",
(*tea)[i].number, (*tea)[i].name, (*tea)[i].sex, (*tea)[i].addr, (*tea)[i].married);
}
numtea = count;
fclose(fp);
puts("讀取成功");
return 0;
}
void menu() {
puts("\n=== 教師資訊管理系統 ===");
puts("1. 新增教師");
puts("2. 顯示全部");
puts("3. 查詢教師");
puts("4. 修改/刪除");
puts("5. 存檔 (文字)");
puts("6. 讀檔 (文字)");
puts("0. 離開");
printf("選擇: ");
}
int main() {
tea = malloc(INIT_CAPACITY * SIZE);
lens = INIT_CAPACITY;
if (!tea) return 1;
int choice;
while (1) {
menu();
scanf("%d", &choice);
while (getchar() != '\n'); // 清緩衝區
switch (choice) {
case 1: add_tea(&tea); break;
case 2: display_tea(tea, 0, numtea-1); break;
case 3: find_tea(tea); break;
case 4: adjust_tea(tea); break;
case 5: write_txt(tea); break;
case 6: read_txt(&tea); break;
case 0: free(tea); puts("再見"); return 0;
default: puts("無效選項");
}
}
}
| 技術點 | 應用處 | 關鍵收穫 |
|---|---|---|
| 動態陣列 + realloc | 教師陣列擴容 | 二級指標更新基址、realloc 安全模式 |
| 結構體陣列操作 | 增刪改查、排序 | 整體賦值移動、字串欄位處理 |
| 安全字串輸入 | 所有輸入 | fgets + 去換行、避免 gets/scanf 緩衝區殘留 |
| 檔案 IO 兩種模式 | 文字/二進位 | fprintf/fscanf 可讀性;fwrite/fread 效能 |
qsort 實現多欄位排序teacher.h/c, storage.h/c, ui.h/c_wfopen / SetConsoleOutputCP(65001))此專案展示了 C 語言處理中型資料管理應用的完整骨架,是從「寫語法練習」邁向「寫工程級程式」的優秀範例。