結構體(struct)將不同類型的變量組合為單一實體,是 C 語言實現抽象數據類型(ADT)的核心機制。本文系統覆蓋定義、初始化、嵌套、數組、指針操作、內存對齊、模塊化拆分與工程實踐。
// 1. 標準定義 + 類型別名
typedef struct {
char name[32];
int age;
float score;
struct { int y, m, d; } birthday; // 匿名嵌套結構體
} Student;
// 2. 多種初始化方式
Student s1 = {"Alice", 20, 92.5, {2004, 5, 17}}; // 順序初始化
Student s2 = {.name="Bob", .score=88.0, .age=21}; // C99 指定初始化器
Student s3 = s1; // 整體賦值(淺拷貝)
| 場景 | 語法 | 本質 |
|---|---|---|
| 結構體變量 | s.age | 直接偏移訪問 |
| 結構體指針 | p->age 等價 (*p).age | 解引用後偏移 |
Student class[3] = {
{"Zhang", 23, 90, {1990,9,28}},
{"Zuo", 25, 88, {1991,9,20}},
{"Fang", 24, 89, {1992,8,8}}
};
// 遍歷、查找最高分、最小年齡
Student *max = &class[0], *min = &class[0];
for (int i=1; i<3; ++i) {
if (class[i].score > max->score) max = &class[i];
if (class[i].age < min->age) min = &class[i];
}
Student *p = malloc(sizeof *p);
if (p) {
strcpy(p->name, "Dynamic");
p->age = 22;
free(p);
}
// 鏈表節點自引用
typedef struct Node {
int data;
struct Node *next; // 必須用 struct Node*,typedef 名尚未生效
} Node;
// 傳值:拷貝整個結構體,修改不影響原值,大結構體開銷大
void printByVal(Student s) { printf("%s\n", s.name); }
// 傳指針:傳遞地址,可修改原值,高效,推薦常用
void printByPtr(const Student *s) { printf("%s\n", s->name); }
void modify(Student *s) { s->score += 5; }
struct Demo {
char c; // 1 byte
// 3 bytes padding
int i; // 4 bytes
// 0 bytes padding
double d; // 8 bytes
};
// sizeof(Demo) = 24 (非 13)
// 對齊規則:每個成員偏移為其類型大小的倍數,結構體總大小為最大對齊數的倍數
// 優化前 24 bytes
struct Bad { char c; int i; double d; };
// 優化後 16 bytes(double 8 對齊,int 4,char 1+3 padding)
struct Good { double d; int i; char c; };
student/ ├── include/ │ └── student.h // 類型定義、接口聲明 ├── src/ │ ├── student.c // 實現 │ └── sort.c // 排序算法 └── main.c
#ifndef STUDENT_H
#define STUDENT_H
typedef struct {
char name[32];
int age;
float score;
struct { int y, m, d; } birthday;
} Student;
// 接口聲明
void student_print(const Student *s);
void student_sort_by_score(Student *arr, int n); // 降序
int student_find_top(const Student *arr, int n); // 返回最高分索引
#endif
#include "student.h" #include#include void student_print(const Student *s) { printf("%s, %d, %.2f, %04d-%02d-%02d\n", s->name, s->age, s->score, s->birthday.y, s->birthday.m, s->birthday.d); } void student_sort_by_score(Student *arr, int n) { // 簡單選擇排序,實際可用 qsort for (int i=0; i arr[max_idx].score) max_idx = j; if (max_idx != i) { Student tmp = arr[i]; arr[i] = arr[max_idx]; arr[max_idx] = tmp; } } }
隱藏結構體定義,僅在 .c 中暴露,頭文件只聲明前向標籤,實現真正的封裝與編譯防火牆。
// api.h
typedef struct Context Context; // 前向聲明
Context* ctx_create(void);
void ctx_destroy(Context *c);
int ctx_do_work(Context *c, int input);
// api.c
#include "api.h"
struct Context { // 完整定義隱藏在 .c
int state;
double cache[1024];
};
Context* ctx_create() { return calloc(1, sizeof(struct Context)); }
void ctx_destroy(Context *c) { free(c); }
int ctx_do_work(Context *c, int input) { return c->state += input; }
char data[];,配合 malloc(sizeof(Struct) + len) 實現變長結構體unsigned int flag:1; 節省空間,但不可取地址、跨平台佈局不可移植memcmp(需先置零 padding)struct Node { struct Node *next; }; 必須用標籤名,typedef 別名在定義完成前不可用__attribute__((packed)) 強制 1 字節對齊,網絡協議/硬件寄存器映射常用結構體是 C 語言從「數據+函數」走向「面向對象雛形」的關鍵。精通定義/初始化、指針操作、內存對齊、模塊化拆分、不透明指針封裝,配合const 正確性、所有權語義(誰分配誰釋放)、錯誤碼返回,即可構建健壯、可維護的系統級代碼庫。