一、联合体(Union)
1.1 联合体的声明
联合体是一个或多个成员组成,其中成员可以是不同类型,并且所有成员共用同一块内存空间,所以联合体也称为共用体。联合体声明的关键字是union。
//联合体类型的声明 union Un { char c; int i; }; int main() { union Un un;//联合体的定义 sizeof("%d\n,sizeof(un)");-->4 return 0; }
1.2 联合体的特点
联合体的特点:
- 联合体所有成员共用同一块内存空间,一个联合体变量的大小,至少是最大成员的大小,编译器只为最大的成员分配足够大的空间
- 给联合体其中一个成员赋值,其他成员的值也会变化
代码一 union Un { char c; int i; }; int main() { union Un un = {0}; // 下⾯输出的结果是⼀样的吗? printf("%p\n", &(un.i)); printf("%p\n", &(un.c)); printf("%p\n", &un); return 0; } 结果:001AF85C 001AF85C 001AF85C
代码二 union Un { char c; int i; }; int main() { union Un un = {0}; un.i = 0x11223344; un.c = 0x55; printf("%x\n", un.i); return 0; } 结果:11223355
【说明】:
从代码一输出的结果是相同的,说明联合体中成员变量是一块空间存储。从代码二输出中,第四个字节的内容被修改位55,对联合体中成员赋值,会影响联合体的全部成员,char类型只占用一个字节。
1.3 结构体和联合体内存布局对比
struct Su { char c; int i; }S; Union Un { char c; int i; }un;
【说明】:结构体是通过以为空间换取时间设计("浪费"空间),而联合体是节省空间
1.4 计算联合体的大小
【规则如下】:
- 联合体的大小至少是最大成员的大小
- 当最大成员大小(联合体总大小)要对齐到最大对齐数的整数倍
#include <stdio.h> union Un1 { char c[5]; 1 8 1//最大成员大小为5 int i; 4 8 4 };//最大对齐数位4,最大成员大小为5,对齐到8是最大对齐数的整数倍 union Un2 { short c[7]; 2 8 2//最大成员大小为7 int i; 4 8 4 };//最大对齐数位4,最大成员大小为7,对齐到16是最大对齐数的整数倍 int main() { printf("%d\n", sizeof(union Un1));-->8 printf("%d\n", sizeof(union Un2));-->16 return 0; }
1.5 联合体的实际使用样例
场景如下,搞一个礼品兑换,礼品兑换单中有三种商品:图书、被子、衬衫。每一种商品的信息:库存量、价格等相关的其他信息
- 图书:书名、作者、页数
- 杯子:设计
- 衬衫:设计、可选颜色、尺寸
如果单纯采用结构体,将变量全部写下来:
struct list { int stock_number;//库存量 double price;//定价 int item_type;//商品类型 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; };
【说明】:
将不同商品的本身属性使用联合体处理,初始化的时候是初始化一个联合体对象,联合体里面只支持初始化一个结构体成员,没法加中括号,再在中括号里面分别初始化,语法不允许。
而且这种情况我们正常使用时本身就只会选择一个成员进行初始化。如果需要初始化多个对象,则将其放进一个结构体数组初始化,每个数组元素都是结构体单独初始化
【联合体习题】:判断当前机器是大端还是小端(通过数字和地址关系)
int check_sys() { union { int i; char c; } un.i=1;//00 00 00 01-->01 00 00 00(小端) return un.c }
二、Enum(枚举)
枚举顾名思义就是⼀>列举
列举可能的取值:用于定义一组具有离散值的常量,使数据更简洁、方便使用,关枚举的关键字enum。
2.1 枚举类型的声明
比如列举:星期一到星期日十有限的七天或者性别、颜色等。
enum Day { Mon, Tues, Wed, Thur, Fri, Sat, Sun }; enum Sex { Man; Woman; }
以上定义的enum Day、enum Sex都是枚举类型,而{}中枚举类型的可能取值称为枚举常量
在声明枚举类型的时候,可以同时对枚举常量初始化(枚举常量都是有值的,默认从0开始,并且依次递增)
enum Sex { Man=10; Woman;//11 }
2.2 枚举类型的优点
【问题】:
这里就像#define定义常量,为什么还有枚举这样子数据类型
【枚举的优点】:
- 增加代码的可读性和可维护性
- 枚举类型有类型检查,更加严谨
- 便于调试,预处理阶段会删除#define定义的符号
- 一次可以定义多个枚举常量,使用方便
- 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
2.3 枚举类型的使用
enum Color { Red=2; Blue=1; } enum Color clr=Blue;
使用枚举常量给枚举变量赋值,也可以拿整数给枚举变量赋值(C语言可以,C++不行,C++检查比较严格)
【使用场景】:
比如:在枚举类型中定义事物的编号,通过输入数字(编号),找到对应的事情
enum color { Red=1; Blue;//1递增为2 } int main() { enum color favorite_color; printf("请输入你喜欢的颜色:(1.Red,2.Bule)"); //输入1或2去代表Red或Bule scanf("%u",&favorite_color); switch(favorite_color) { case Red(Red==1): printf("%你喜欢的颜色为红色"); break; case Blue(Blue==2): printf("%你喜欢的颜色为蓝色"); break; default : printf("没有喜欢的颜色") } }
【说明】:这里不是输入枚举对象,而是枚举对象对应的数字。