[C]基础18.自定义类型:联合和枚举
- 博客主页:向不悔
- 本篇专栏:[C]
- 您的支持,是我的创作动力。
文章目录
- 0、总结
- 1、联合体(共用体)
- 1.1 联合体类型的声明
- 1.2 联合体的特点
- 1.3 联合体大小的计算
- 1.4 联合体的应用场景
- 1.5 联合体练习:判断机器字节序
- 2、枚举类型
- 2.1 枚举类型的声明
- 2.2 枚举常量的值
- 2.3 枚举类型的优点
- 2.4 枚举类型的使用
- 3、其他
- 3.1 深入理解:联合体的内存布局
- 3.2 枚举与#define的比较
- 3.3 实际应用建议
0、总结
1、联合体(共用体)
1.1 联合体类型的声明
联合体(union)是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。与结构体类似,联合体也是由一个或多个成员组成,但这些成员共享同一块内存空间。
union Un {char c;int i;
};
1.2 联合体的特点
- 内存共享:所有成员共用同一块内存空间
- 大小确定:联合体的大小至少是其最大成员的大小
- 值覆盖:给一个成员赋值会影响其他成员的值
示例代码1:
union Un un = { 0 };printf("%p\n", &(un.i)); // 输出地址printf("%p\n", &(un.c)); // 输出相同地址printf("%p\n", &un); // 输出相同地址
运行:
0000005805BBFA24
0000005805BBFA24
0000005805BBFA24
示例代码2(展示值覆盖):
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i); // 输出修改后的值
运行:
11223355
1.3 联合体大小的计算
- 联合体大小至少是最大成员的大小
- 当最大成员大小不是最大对齐数的整数倍时,需要对齐到最大对齐数的整数倍
示例:
union Un1 {char c[5]; // 大小5,对齐数1int i; // 大小4,对齐数4
}; // 最终大小为8(对齐到4的倍数)union Un2 {short c[7]; // 大小14,对齐数2int i; // 大小4,对齐数4
}; // 最终大小为16(对齐到4的倍数)
1.4 联合体的应用场景
使用联合体是可以节省空间的,举例:比如,我们要搞一个活动,要上线一个礼品兑换单,礼品兑换单中有三种商品:图书、杯子、衬衫。每一种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、页数
杯子:设计
衬衫:设计、可选颜色、可选尺寸
问题描述:如果直接将所有属性都放在一个结构体中,会导致结构体大小偏大,浪费内存。因为对于每种商品来说,只有部分属性是常用的。例如,图书不需要设计、颜色、尺码等属性。
解决方案:将公共属性单独提取出来,将每种商品独特的属性放入联合体中。这样可以减少结构体的大小,节省内存空间。
礼品兑换单示例:
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;
};
1.5 联合体练习:判断机器字节序
int check_sys() {union {int i;char c;} un;un.i = 1;return un.c; // 返回1是小端,返回0是大端
}
2、枚举类型
2.1 枚举类型的声明
枚举类型用于定义一组命名的整数常量,使代码更易读。
enum Day { Mon, Tues, Wed, Thur, Fri, Sat, Sun };
enum Sex { MALE, FEMALE, SECRET };
enum Color { RED, GREEN, BLUE };
2.2 枚举常量的值
- 默认从0开始,依次递增1
- 可以手动指定初始值
enum Color { RED=2, GREEN=4, BLUE=8 };
2.3 枚举类型的优点
-
可读性和可维护性:使用有意义的名称代替数字
-
类型检查:比
#define
定义的常量更安全 -
调试友好:枚举常量在调试时可见,而
#define
宏在预处理阶段已被替换 -
作用域规则:遵循常规的作用域规则
-
批量定义:可以一次定义多个相关常量
2.4 枚举类型的使用
enum Color clr = GREEN; // 正确使用
// clr = 2; // 在C中可能允许,但在C++中不允许
3、其他
3.1 深入理解:联合体的内存布局
联合体的所有成员共享同一块内存空间,这意味着:
-
内存覆盖:修改一个成员会覆盖其他成员的值
-
类型解释:同一内存块可以用不同数据类型解释
例如,在判断字节序的例子中,我们利用联合体的这一特性,通过int和char两种类型访问同一内存位置,从而判断系统是小端序(低位字节在前)还是大端序(高位字节在前)。
3.2 枚举与#define的比较
虽然#define
也可以定义常量,但枚举有以下优势:
- 类型安全:枚举常量有明确的类型,编译器可以进行类型检查
- 调试信息:枚举常量在调试器中可见,而宏定义已被预处理替换
- 作用域控制:枚举可以定义在函数内部,限制作用域
- 自动赋值:枚举可以自动递增赋值,减少手动赋值的工作量
3.3 实际应用建议
联合体:
- 用于需要节省内存的场景
- 处理不同类型数据的互斥访问
- 实现类似C++中variant的类型安全联合
枚举:
- 替代魔法数字,提高代码可读性
- 表示有限的状态集合
- 作为函数参数限制输入范围
总结:
- 联合体和枚举是C语言中强大的自定义类型工具。
- 联合体通过内存共享实现灵活的数据表示,特别适合内存敏感的应用。
- 枚举则通过命名常量提高了代码的可读性和安全性。
- 理解它们的特性和适用场景,可以帮助我们编写更高效、更易维护的C语言代码。