在C語言中,自定義類型為數據組織提供了極大的靈活性。除了常用的結構體,聯合體(共用體)和枚舉也是非常重要的自定義類型。本文將結合實例,詳細解析聯合體和枚舉的特性、用法及實際應用場景。
一、聯合體(Union):共用內存的特殊類型
聯合體(又稱共用體)是一種特殊的自定義類型,它的所有成員共用同一塊內存空間,這一特性使其在內存優化場景中非常實用。
1.1 聯合體的聲明與定義
聯合體的聲明語法與結構體類似,但成員的內存布局完全不同。
// 聯合體類型聲明
union Un {char c; // 字符型成員int i; // 整型成員
};int main() {// 聯合體變量定義并初始化union Un un = {0}; // 計算聯合體大小printf("聯合體大小:%d\n", sizeof(un)); // 輸出結果:4return 0;
}
為什么輸出結果是4?
因為聯合體的大小至少是最大成員的大小,上述代碼中int
類型成員i
占4字節,因此聯合體大小為4。
1.2 聯合體的核心特點:成員共用內存
聯合體最核心的特性是所有成員共用同一塊內存空間,這意味著:
- 聯合體變量的地址與各成員的地址相同;
- 給一個成員賦值,可能會覆蓋其他成員的值。
實例1:驗證成員地址相同
#include <stdio.h>
union Un {char c;int i;
};int main() {union Un un = {0};// 打印聯合體變量及成員的地址printf("&un:%p\n", &un);printf("&un.i:%p\n", &un.i);printf("&un.c:%p\n", &un.c);return 0;
}
輸出結果:三個地址完全相同,證明成員共用同一塊內存。
實例2:成員賦值的相互影響
#include <stdio.h>
union Un {char c;int i;
};int main() {union Un un = {0};un.i = 0x11223344; // 給整型成員賦值un.c = 0x55; // 給字符型成員賦值(覆蓋低字節)printf("un.i = %x\n", un.i); // 輸出結果:11223355return 0;
}
解析:
int
類型在內存中占4字節,0x11223344
的內存布局為(假設小端存儲):44 33 22 11
;char
類型僅占1字節(低地址字節),賦值0x55
后覆蓋了低字節,內存變為55 33 22 11
,因此un.i
最終為0x11223355
。
1.3 結構體與聯合體的內存布局對比
類型 | 內存布局特點 | 示例大小(char+int) |
---|---|---|
結構體(struct) | 成員按順序存儲,存在內存對齊浪費 | 8字節(1+3對齊+4) |
聯合體(union) | 成員共用內存,無對齊浪費 | 4字節(取最大成員大小) |
內存布局示意圖:
- 結構體:
[char][3字節對齊浪費][int]
- 聯合體:
[char和int共用4字節空間]
1.4 聯合體大小的計算規則
聯合體的大小需滿足兩個條件:
- 至少是最大成員的大小(保證能容納最大成員);
- 必須是最大對齊數的整數倍(內存對齊要求)。
對齊數定義:
每個成員的對齊數 = 成員自身大小與編譯器默認對齊數(通常為8)的較小值。
實例計算:
#include <stdio.h>// 案例1:char[5]與int
union Un1 {char c[5]; // 大小5,對齊數1(char大小1)int i; // 大小4,對齊數4(int大小4)
};// 案例2:short[7]與int
union Un2 {short c[7]; // 大小14(2*7),對齊數2(short大小2)int i; // 大小4,對齊數4(int大小4)
};int main() {printf("Un1大小:%d\n", sizeof(union Un1)); // 輸出:8printf("Un2大小:%d\n", sizeof(union Un2)); // 輸出:16return 0;
}
計算過程:
- Un1:最大成員大小5,最大對齊數4。5不是4的倍數,向上對齊到8(4×2);
- Un2:最大成員大小14,最大對齊數4。14不是4的倍數,向上對齊到16(4×4)。
1.5 聯合體的實用場景
場景1:節省內存空間
當不同屬性不會同時使用時,用聯合體共用內存可大幅減少內存占用。例如禮品兌換單設計:
// 優化前:結構體包含所有屬性,內存浪費
struct gift_list_old {int stock_number; // 庫存量double price; // 定價int item_type; // 商品類型char title[20]; // 書名(僅圖書用)char author[20]; // 作者(僅圖書用)char design[30]; // 設計(僅杯子/襯衫用)int colors; // 顏色(僅襯衫用)
};// 優化后:用聯合體存儲差異化屬性
struct gift_list {int stock_number; // 公共屬性double price; int item_type; union { // 差異化屬性共用內存struct { char title[20]; char author[20]; } book; // 圖書struct { char design[30]; } mug; // 杯子struct { char design[30]; int colors; } shirt; // 襯衫} item;
};
場景2:判斷機器字節序(大小端)
利用聯合體成員共用內存的特性,可簡單判斷機器存儲方式:
// 返回1:小端存儲;返回0:大端存儲
int check_sys() {union {int i; // 4字節整型char c; // 1字節字符} un;un.i = 1; // 內存存儲為0x00000001(大端)或0x01000000(小端)return un.c; // 小端返回1,大端返回0
}
二、枚舉類型(Enum):常量的有序集合
枚舉類型用于定義一組具有離散值的常量,使代碼更具可讀性和可維護性。
2.1 枚舉類型的聲明與初始化
枚舉通過enum
關鍵字聲明,其中的成員稱為枚舉常量。
// 基本聲明(默認值從0開始遞增)
enum Day {Mon, // 0Tues, // 1Wed, // 2Thur, // 3Fri, // 4Sat, // 5Sun // 6
};// 自定義初始值(后續值依次遞增)
enum Color {RED = 2, // 2GREEN = 4, // 4BLUE = 8 // 8
};
2.2 枚舉的優點:為何不用#define?
與#define
定義常量相比,枚舉具有明顯優勢:
- 增強可讀性:枚舉常量有明確的類型歸屬,代碼邏輯更清晰;
- 類型檢查:枚舉是強類型,編譯器會進行類型校驗(
#define
無類型); - 便于調試:枚舉常量在調試階段可見(
#define
在預處理階段被替換,調試中無符號); - 批量定義:一次可定義多個相關常量,無需重復寫
#define
; - 作用域規則:枚舉聲明在函數內時,僅在函數內有效,避免命名沖突。
2.3 枚舉類型的使用
枚舉變量需用枚舉常量賦值,C語言允許整數賦值但不推薦(C++嚴格禁止)。
enum Color {RED = 1,GREEN = 2,BLUE = 4
};int main() {enum Color clr = GREEN; // 正確:用枚舉常量賦值// enum Color clr2 = 2; // C允許,C++禁止(類型不匹配)return 0;
}
三、總結
- 聯合體通過成員共用內存實現內存優化,適用于不同屬性不同時使用的場景,大小計算需滿足最大成員大小和對齊要求;
- 枚舉用于定義離散常量集合,相比
#define
具有更強的類型安全性和可讀性,是提升代碼質量的重要工具。
合理使用聯合體和枚舉,能讓C語言代碼更高效、更易維護,尤其在嵌入式開發、內存受限場景中發揮重要作用。