| 特性 | 結構體 | 聯合體 |
| 記憶體佈局 | 成員依序排列,各自佔用獨立空間 | 成員完全重疊,共享同一塊記憶體 |
| 總大小 | 所有成員大小之和(含對齊填充) | 等於最大成員的大小(含對齊) |
| 同時存活 | 所有成員同時存在 | 同一時刻僅一個成員有效 |
| 賦值行為 | 各成員獨立賦值 | 新值覆蓋舊值 |
聯合體定義語法
union 聯合體名 {
型別 成員1;
型別 成員2;
// ...
};
範例:人員資料(教師/學生互斥欄位)
union PerData {
int class_id; // 學生:班級編號
char office[10]; // 教師:教研室名稱
};
變數宣告三種形式
/* 1. 先定義後宣告 */
union PerData { int class_id; char office[10]; };
union PerData a, b;
/* 2. 定義同時宣告 */
union PerData { int class_id; char office[10]; } a, b;
/* 3. 匿名聯合體直接宣告 */
union { int class_id; char office[10]; } a, b;
成員存取與初始化規則
- 存取語法:
變數名.成員名 或 指標->成員名
- 禁止 對整個聯合體變數直接賦值(
a = b 在 C 標準中未定義)
- 禁止 宣告時初始化(
union PerData u = {1}; 非標準)
- 賦值只能針對單一成員進行
完整實戰範例:教師/學生通用表單
#include <stdio.h>
int main() {
struct {
char name[10];
int age;
char job; // 's' = 學生, 't' = 教師
union {
int class_id;
char office[10];
} dept;
} person[2];
for (int i = 0; i < 2; i++) {
printf("輸入: 姓名 年齡 身份(s/t) 班級/教研室: ");
scanf("%s %d %c", person[i].name, &person[i].age, &person[i].job);
if (person[i].job == 's')
scanf("%d", &person[i].dept.class_id); // 陣列/指標無需 &
else
scanf("%s", person[i].dept.office); // 字串陣列名即地址
}
printf("\n%-10s %3s %3s %s\n", "姓名", "年齡", "身份", "班級/教研室");
for (int i = 0; i < 2; i++) {
if (person[i].job == 's')
printf("%-10s %3d %3c %d\n", person[i].name, person[i].age, person[i].job, person[i].dept.class_id);
else
printf("%-10s %3d %3c %s\n", person[i].name, person[i].age, person[i].job, person[i].dept.office);
}
return 0;
}
進階特性與限制
匿名聯合體(C11 標準)
struct Widget {
int type;
union { // 無標籤,成員直接提升到結構體作用域
int i;
float f;
char *s;
};
};
// 存取:w.i, w.f, w.s (無需 w.u.i)
嵌套與對齊
- 聯合體可嵌套結構體,結構體可嵌套聯合體
- 大小遵循最大成員對齊:
union { char c; double d; } 大小為 8 的倍數
函數參數與回傳限制
| 操作 | 結構體 | 聯合體 |
| 值傳遞參數 | ✅ 支援 | ❌ C 標準不保證(建議用指標) |
| 回傳值 | ✅ 支援 | ❌ 同上 |
| 指標傳遞 | ✅ | ✅ 推薦做法 |
記憶體佈局視覺化
union PerData { int class_id; char office[10]; };
假設 int=4 bytes, 對齊=4
記憶體位址: 基址 基址+1 基址+2 基址+3 基址+4 ... 基址+9
class_id 佔用: [ byte0 ][ byte1 ][ byte2 ][ byte3 ]
office 佔用: [ o ][ f ][ f ][ i ][ c ][ e ][\0][ ][ ][ ]
▲ ▲
同一塊記憶體區域(10 bytes,按最大成員對齊) ▲
適用場景總結
- 互斥資料欄位:同一實體不同型態下的不同屬性(如上例)
- 型別轉換/位操作:
union { float f; uint32_t u; } 觀察 IEEE 754 位模式
- 硬體暫存器映射:嵌入式開發中,同一位址不同位域含義
- 節省記憶體:大量物件中僅部分欄位同時生效
避坑提醒
- 寫入
class_id 後立即讀取 office → 未定義行為(垃圾資料)
- 依賴成員偏移量進行指標運算 → 違反嚴格別名規則,可能導致優化錯誤
- 跨平台傳輸聯合體資料 → 位元組序、對齊、型別大小差異導致解析錯誤
- 上一篇文章: Shell 腳本實現無限迴圈的三種寫法與最佳實踐
- 下一篇文章: C 語言嵌入 Python 完整指南:從初始化到呼叫函數與傳遞參數
Copyright ©
程式碼之家 All Rights Reserved