一、聯合體定義與四大核心特性
《C Programming Language》定義:
- 聯合體是一個結構;
- 所有成員相對基地址偏移量為 0 (共享同一記憶體起始位置);
- 佔用空間足以容納最「寬」的成員;
- 對齊方式需滿足所有成員的對齊要求。
二、大小計算規則:不只是最大成員
union U1 { char s[9]; int n; double d; }; // sizeof = 16
union U2 { char s[5]; int n; double d; }; // sizeof = 8
規則:sizeof(union) = max( max_member_size, LCM(alignment_requirements) ) 向上取整到最大對齊值的倍數。
- U1:最大成員
double (8) vs char[9] (9) → 至少 9。對齊需求:1, 4, 8 → LCM=8。9 非 8 倍數 → 擴至 16。
- U2:最大成員 8,對齊 8,8 是 8 倍數 → 8。
三、記憶體佈局視覺化
union U { int i; float f; char c[4]; };
// 記憶體 4 bytes:
+----+----+----+----+
| i0 | i1 | i2 | i3 | <- int i (4 bytes)
+----+----+----+----+
| f0 | f1 | f2 | f3 | <- float f (同 4 bytes)
+----+----+----+----+
| c0 | c1 | c2 | c3 | <- char c[4] (同 4 bytes)
+----+----+----+----+
所有成員起始位址相同 = 聯合體變數位址
四、型別懲罰與位模式重新解讀
聯合體合法用途:以一種型別寫入,以另一種型別讀取,檢查位模式。
union { float f; uint32_t u; } converter;
converter.f = 1.0f;
printf("0x%08X\n", converter.u); // 0x3F800000 (IEEE 754)
// 檢查端序
union { uint16_t s; char c[2]; } endian;
endian.s = 0x0102;
printf("%s\n", endian.c[0] == 1 ? "Big-Endian" : "Little-Endian");
五、標準規範與未定義行為邊界
| 操作 | C99/C11 | 備註 |
| 寫入成員 A,讀取成員 B (同類型大小) | 定義行為 | 常見型別懲罰 |
| 寫入成員 A,讀取成員 B (不同大小) | 部分定義 | 讀取共同部分定義,超出部分未指定值 |
| 寫入非最後存儲的成員 | 未定義行為 | 嚴格別名規則違反 (除非用 union) |
| 透過指標強制轉型存取 | 未定義行為 | 違反嚴格別名,必須用 union 或 memcpy |
六、實戰應用場景
1. 協議封包解析 (網路位序 ↔ 主機序)
union Packet {
struct {
uint16_t len;
uint8_t type;
uint8_t flags;
uint32_t seq;
} fields;
uint8_t raw[8]; // 直接收發
};
2. 硬體暖存器映射
union Reg {
uint32_t val;
struct {
uint32_t enable:1;
uint32_t mode:3;
uint32_t reserved:28;
} bits;
};
3. 變長資料標記 (Tagged Union)
enum Type { INT, FLOAT, STR };
union Value { int i; float f; char *s; };
struct TaggedValue {
enum Type type;
union Value val;
};
4. 記憶體節省:互斥欄位
struct Event {
enum { KEY, MOUSE, NET } kind;
union {
struct { int key; int mods; } key;
struct { int x, y; int btn; } mouse;
struct { int fd; char *buf; } net;
} data;
};
七、匿名聯合體 (C11/GNU 擴展)
struct Sensor {
int id;
union { // 無標籤,成員直接提升到結構體作用域
float temp;
int pressure;
double humidity;
};
};
// 存取: sensor.temp (而非 sensor.u.temp)
八、對齊屬性控制
// 強制 16 位元組對齊 (SSE/AVX)
union alignas(16) AlignedUnion { double d; char pad[16]; };
// 封裝對齊 (GCC/Clang)
union __attribute__((packed)) PackedUnion { char c; int i; }; // sizeof=5, 可能效能下降
九、總結
- 聯合體 = 共享記憶體 + 型別懲罰合法通道。
- 大小 = max(最大成員, 對齊值 LCM 向上取整)。
- 嚴格別名規則下,聯合體是少數合法的型別懲罰方式。
- 廣泛用於協議解析、硬體映射、標記聯合、記憶體緊湊結構。
- 上一篇文章: C 語言動態記憶體管理核心機制:空閒鏈表、分配演算法與邊界標記法
- 下一篇文章: Objective-C 類別建構完整實戰:介面宣告、實作、屬性與記憶體管理
Copyright ©
程式碼之家 All Rights Reserved