本文呈現一個工業級的 C 語言結構體數組管理完整模型,涵蓋動態分配、鍵盤輸入、按姓名排序、格式化輸出、內存銷毀全流程。代碼採用「雙指針傳遞所有權」模式,嚴格區分客戶端與服務端職責,可直接作為項目模板複用。
AdvTeacher:姓名、職稱、年齡、地址、學生列表(動態數組)。#define TEACHER_COUNT 3
#define STUDENT_PER_TEACHER 1
typedef struct AdvTeacher {
char *name;
char *title;
int age;
char *addr;
char **student; // 動態字符串數組
} AdvTeacher;
| 函數 | 職責 | 參數約定 |
|---|---|---|
CreateStructArray | 分配結構體數組及所有成員內存 | AdvTeacher **out 雙指針輸出,返回錯誤碼 |
FreeStructArray | 遞歸釋放所有成員及數組本體 | AdvTeacher **inout 釋放後置 NULL |
PrintStructArray | 格式化輸出 | 只讀,不修改 |
SortStructArray | 冒泡排序 (教學用),實際建議 qsort | 原地排序,交換整個結構體 |
int CreateStructArray(AdvTeacher **t, int cnt, int stu_cnt) {
if (!t) return -1;
AdvTeacher *arr = malloc(cnt * sizeof(AdvTeacher));
if (!arr) return -2;
for (int i = 0; i < cnt; ++i) {
arr[i].name = malloc(256);
arr[i].title = malloc(256);
arr[i].addr = malloc(256);
if (!arr[i].name || !arr[i].title || !arr[i].addr) return -3;
arr[i].student = malloc(stu_cnt * sizeof(char*));
if (!arr[i].student) return -4;
for (int j = 0; j < stu_cnt; ++j) {
arr[i].student[j] = malloc(256);
if (!arr[i].student[j]) return -5;
}
}
*t = arr; // 所有權轉移給調用者
return 0;
}
int FreeStructArray(AdvTeacher **t, int cnt, int stu_cnt) {
if (!t || !*t) return 0; // 幂等:重複調用安全
AdvTeacher *arr = *t;
for (int i = 0; i < cnt; ++i) {
for (int j = 0; j < stu_cnt; ++j) {
free(arr[i].student[j]);
arr[i].student[j] = NULL;
}
free(arr[i].student); arr[i].student = NULL;
free(arr[i].name); arr[i].name = NULL;
free(arr[i].title); arr[i].title = NULL;
free(arr[i].addr); arr[i].addr = NULL;
}
free(arr);
*t = NULL; // 切斷調用者持有的指針
return 0;
}
int SortStructArray(AdvTeacher *t, int cnt) {
if (!t) return -1;
for (int i = 0; i < cnt; ++i) {
for (int j = i+1; j < cnt; ++j) {
if (strcmp(t[i].name, t[j].name) > 0) { // 升序
AdvTeacher tmp = t[i]; // 結構體賦值 = 所有指針淺拷貝
t[i] = t[j];
t[j] = tmp;
}
}
}
return 0;
}
int main(void) {
AdvTeacher *teachers = NULL;
int rv = CreateStructArray(&teachers, TEACHER_COUNT, STUDENT_PER_TEACHER);
if (rv) { fprintf(stderr, "Create failed: %d\n", rv); return rv; }
// 輸入階段
for (int i = 0; i < TEACHER_COUNT; ++i) {
printf("第 %d 位老師姓名: ", i+1); scanf("%255s", teachers[i].name);
printf("第 %d 位老師年齡: ", i+1); scanf("%d", &teachers[i].age);
printf("第 %d 位老師職稱: ", i+1); scanf("%255s", teachers[i].title);
printf("第 %d 位老師地址: ", i+1); scanf("%255s", teachers[i].addr);
for (int j = 0; j < STUDENT_PER_TEACHER; ++j) {
printf(" 學生 %d 姓名: ", j+1); scanf("%255s", teachers[i].student[j]);
}
}
PrintStructArray(teachers, TEACHER_COUNT, STUDENT_PER_TEACHER);
SortStructArray(teachers, TEACHER_COUNT);
puts("\n=== 排序後 ===\n");
PrintStructArray(teachers, TEACHER_COUNT, STUDENT_PER_TEACHER);
FreeStructArray(&teachers, TEACHER_COUNT, STUDENT_PER_TEACHER);
return 0;
}
qsort + 比較函數,O(n log n)。scanf("%255s", ...) 限制寬度防溢出;生產環境建議 fgets + 去換行。ERR_OK=0, ERR_NULL_PTR=-1, ERR_OOM=-2... 替代魔術數字。void* + size_t + 操作函數指針 實現通用動態數組庫。--leak-check=full --show-leak-kinds=all 零洩漏。這個模型展示了 C 語言手動內存管理的規範範式:誰分配誰釋放、雙指針傳遞所有權、釋放後置 NULL、錯誤路徑完整清理。掌握此模式,即掌握了 C 語言資源管理的精髓。