【C语言】自定义类型:联合体与枚举
文章目录
- 一、联合体(Union):内存共用的灵活类型
- 1.1 什么是联合体?
- 1.2 联合体的核心特点
- 1.3 联合体与结构体的对比
- 1.4 联合体大小的计算规则
- 1.5 联合体的典型应用场景
- 场景1:节省内存(互斥属性存储)
- 场景2:判断机器字节序
- 二、枚举(Enum):有限取值的清晰表示
- 2.1 什么是枚举?
- 2.2 枚举的优势(对比#define)
- 2.3 枚举的使用方法
- 三、总结
在C语言中,自定义类型为数据组织提供了灵活的方式。除了常见的结构体,联合体(共用体)和枚举也是非常实用的类型。它们在内存管理和代码可读性方面有着独特的优势,本文将详细解析这两种类型的特性、用法及应用场景。
一、联合体(Union):内存共用的灵活类型
1.1 什么是联合体?
联合体是一种特殊的自定义类型,它由多个不同类型的成员组成,但所有成员共用同一块内存空间。这意味着联合体的大小仅需满足最大成员的存储需求,相比结构体能显著节省内存。
声明方式与结构体类似,关键字为union
:
// 联合体类型声明
union Un {char c; // 字符型成员int i; // 整型成员
};// 联合体变量定义
union Un un;
1.2 联合体的核心特点
-
内存共用:所有成员共享同一块内存,起始地址相同。
#include <stdio.h> union Un {char c;int i; }; int main() {union Un un;printf("&un = %p\n", &un); // 输出:001AF85Cprintf("&un.i = %p\n", &un.i);// 输出:001AF85Cprintf("&un.c = %p\n", &un.c);// 输出:001AF85Creturn 0; }
上述代码中,联合体变量
un
及其成员i
、c
的地址完全相同,证明它们共用内存。 -
成员值相互影响:修改一个成员的值会影响其他成员。
#include <stdio.h> union Un {char c;int i; }; int main() {union Un un;un.i = 0x11223344; // 赋值整型成员un.c = 0x55; // 修改字符型成员printf("%x\n", un.i); // 输出:11223355return 0; }
原理:
int
类型占4字节,char
占1字节。un.c
修改的是un.i
的第一个字节(低地址位),导致un.i
的原值0x11223344
变为0x11223355
。
1.3 联合体与结构体的对比
类型 | 内存布局特点 | 大小计算方式 |
---|---|---|
结构体(struct) | 成员占用独立内存,按对齐规则排列 | 所有成员大小之和(含填充字节) |
联合体(union) | 成员共用一块内存 | 至少为最大成员大小(需满足对齐) |
示例对比:
// 结构体:成员独立占用内存
struct S {char c; // 1字节int i; // 4字节(因对齐,c后填充3字节)
};
// 大小:1 + 3(填充) + 4 = 8字节// 联合体:成员共用内存
union Un {char c; // 1字节int i; // 4字节
};
// 大小:4字节(取最大成员大小)
1.4 联合体大小的计算规则
- 基础规则:联合体大小至少为最大成员的大小。
- 对齐规则:若最大成员大小不是“最大对齐数”的整数倍,需向上对齐到整数倍。
(对齐数:成员自身大小与默认对齐数的较小值,默认对齐数通常为4或8)
示例计算:
#include <stdio.h>// 案例1:char[5](大小5)与int(大小4)
union Un1 {char c[5]; // 对齐数1,大小5int i; // 对齐数4,大小4
};
// 最大成员大小5,最大对齐数4。5不是4的倍数,向上对齐到8(4×2)。
// sizeof(Un1) = 8// 案例2:short[7](大小14)与int(大小4)
union Un2 {short c[7]; // 对齐数2,大小14int i; // 对齐数4,大小4
};
// 最大成员大小14,最大对齐数4。14不是4的倍数,向上对齐到16(4×4)。
// sizeof(Un2) = 16int main() {printf("%d\n", sizeof(union Un1)); // 输出:8printf("%d\n", sizeof(union Un2)); // 输出:16return 0;
}
1.5 联合体的典型应用场景
场景1:节省内存(互斥属性存储)
当数据包含“公共属性”和“互斥的特殊属性”时,用联合体存储特殊属性可减少内存浪费。
例如“礼品兑换单”设计:
// 优化前:结构体包含所有属性,浪费内存
struct gift_list {// 公共属性int stock_number; // 库存量double price; // 价格int item_type; // 商品类型(1-图书,2-杯子,3-衬衫)// 特殊属性(互斥,仅一种生效)char title[20]; // 书名(仅图书)char author[20]; // 作者(仅图书)int num_pages; // 页数(仅图书)char design[30]; // 设计(仅杯子/衬衫)int colors; // 颜色(仅衬衫)int sizes; // 尺寸(仅衬衫)
};// 优化后:用联合体存储特殊属性
struct gift_list {int stock_number;double price;int item_type;union { // 特殊属性共用内存struct { char title[20]; char author[20]; int num_pages; } book;struct { char design[30]; } mug;struct { char design[30]; int colors; int sizes; } shirt;} item;
};
优化后,特殊属性仅占用最大成员的内存(如shirt
结构体大小),避免了无效属性的内存浪费。
场景2:判断机器字节序
字节序是数据在内存中的存储顺序(大端/小端),联合体可简单判断:
// 返回1:小端(低地址存低位数据);返回0:大端(低地址存高位数据)
int check_sys() {union {int i; // 4字节:0x00000001char c; // 1字节:取i的低地址位} un;un.i = 1;return un.c; // 小端:c=1;大端:c=0
}
二、枚举(Enum):有限取值的清晰表示
2.1 什么是枚举?
枚举用于定义有限个可能取值的集合,例如星期、性别、颜色等。它将离散值命名,增强代码可读性。
声明方式:
// 枚举类型声明(默认值从0开始递增)
enum Day {Mon, // 0Tues, // 1Wed, // 2Thur, // 3Fri, // 4Sat, // 5Sun // 6
};// 手动指定枚举值
enum Color {RED = 2, // 2GREEN = 4, // 4BLUE = 8 // 8
};
enum Day
、enum Color
为枚举类型,Mon
、RED
等为枚举常量。
2.2 枚举的优势(对比#define)
优势 | 说明 |
---|---|
增强可读性 | 用RED 代替1 ,代码意图更清晰。 |
类型检查更严格 | 枚举变量只能接收枚举常量,#define 定义的常量无类型限制。 |
便于调试 | 预处理阶段会删除#define 符号,而枚举常量在调试时可见。 |
批量定义常量 | 一次声明多个相关常量,无需重复写#define 。 |
遵循作用域规则 | 枚举声明在函数内时,仅在函数内有效,避免全局命名污染。 |
2.3 枚举的使用方法
-
定义枚举变量:
enum Color { RED, GREEN, BLUE }; enum Color clr = GREEN; // 用枚举常量赋值
-
赋值规则:
- C语言允许直接赋值整数(不推荐,破坏类型检查):
enum Color clr = 2; // C中允许,C++中禁止
- 建议始终使用枚举常量赋值,保证代码规范性。
- C语言允许直接赋值整数(不推荐,破坏类型检查):
三、总结
- 联合体:通过成员共用内存实现内存优化,适合存储互斥属性,大小计算需考虑最大成员和对齐规则。
- 枚举:清晰表示有限取值集合,相比
#define
更安全、易维护,适合定义状态码、选项等。
合理使用这两种类型,能让代码更高效、更易读,尤其在嵌入式开发(内存受限)和大型项目(代码规范性要求高)中尤为重要。