文章目录
- 前言
- 一、联合体
- 联合体的声明
- 联合体的特点
- 联合体和结构体内存布局对比
- 联合体的大小计算
- 联合体的实际使用样例
- 礼品兑换单
- 判断当前机器是大端还是小端
- 二、枚举
- 枚举的定义
- 枚举类型的声明
- 枚举类型的优点
- 枚举类型的使用
- 总结
前言
关于自定义类型除了我们常用的结构体,其实还有联合与枚举也是属于自定义类型
我们也来学习一下吧!
一、联合体
联合体的声明
联合体是一个或多个成员组成,其中成员可以是不同类型,并且所有成员共用同一块内存空间,所以联合体也称为共用体。联合体声明的关键字是union
//联合体类型的声明
union Un
{// 1 + 4 == 5 吗?char c; // 1int i; // 4
};
int main()
{union Un un;// 联合体的定义sizeof("%zd\n,sizeof(un)"); // 4return 0;
}
联合体的特点
- 联合体所有成员共用同一块内存空间,一个联合体变量的大小,至少是最大成员的大小,编译器只为最大的成员分配足够大的空间
- 给联合体其中一个成员赋值,其他成员的值也会变化
我们不妨来两段代码来详细感受一下这个性质:
// 代码一
union Un
{char c;int i;
};int main()
{union Un un = {0};// 下⾯输出的结果是⼀样的吗?// 结果:001AF85C// 001AF85C// 001AF85Cprintf("%p\n", &(un.i));printf("%p\n", &(un.c));printf("%p\n", &un);return 0;
}
// 代码二
union Un
{char c;int i;
};int main()
{union Un un = {0};un.i = 0x11223344;un.c = 0x55;// 结果:11223355printf("%x\n", un.i);return 0;
}
如果用一幅图来说明,那就是如下:
从代码一输出的结果是相同的,说明联合体中成员变量是一块空间存储
从代码二输出中,第四个字节的内容被修改位55,对联合体中成员赋值,会影响联合体的全部成员,char类型只占用一个字节
还记得前面的内容不,请在十秒内说出这是小端存储还是大端存储?
答案是小端!
联合体和结构体内存布局对比
我们再来比较一下同样成员变量情况下,两种结合方式的区别来加深印象:
struct Su
{char c;int i;
}S;Union Un
{char c;int i;
}un;
结构体是通过以为空间换取时间设计,而联合体是节省空间
联合体的大小计算
- 联合体的大小至少是最大成员的大小
- 当最大成员大小(联合体总大小)要对齐到最大对齐数的整数倍
请注意,很多学习资料都说联合体的大小就是最大成员的大小,其实这是不对的!
事实上,联合体也是有内存对齐的,下面我们就来举例说明
#include <stdio.h>union Un1
{char c[5]; int i;
}; // 最大对齐数位4,最大成员大小为5,对齐到8是最大对齐数的整数倍union Un2
{short c[7];int i;
}; // 最大对齐数位4,最大成员大小为7,对齐到16是最大对齐数的整数倍int main()
{printf("%zd\n", sizeof(union Un1)); // 8printf("%zd\n", sizeof(union Un2)); // 16return 0;
}
联合体的实际使用样例
礼品兑换单
事实上你可以观察下这种做法,把公共属性单独写出来,剩余属于各种商品本身的属性使用联合体处理,其实是在一定程度上节省了内存
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变量的第一个字节的数据单独提取出来,如果最后返回的是1,那说明低字节位放数据低位,事实也确实是这样,也就说明小端是答案
union un
{char c;int i;
};bool check_sys()
{union un u;u.i = 0x00000001;return u.c;
}int main()
{if (check_sys()){printf("小端\n"); // YES}else printf("大端\n");return 0;
}
二、枚举
枚举的定义
枚举顾名思义就是一一列举,而列举可能的取值是用于定义一组具有离散值的常量,使数据更简洁、方便使用,关枚举的关键字enum
枚举类型的声明
enum Day
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};enum Sex
{Man,Woman;
}
以上定义的enum Day、enum Sex都是枚举类型,而{ }中枚举类型的可能取值称为枚举常量
枚举类型的优点
我么可以用宏来定义常量,那么这里为什么还要单独设立一种数据类型呢?
- 增加代码的可读性和可维护性
- 枚举类型有类型检查,更加严谨
- 便于调试,预处理阶段会删除#define定义的符号
- 一次可以定义多个枚举常量,使用方便
- 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
枚举类型的使用
enum Color
{Red=2,Blue=1
}enum Color clr = Blue;
enum Color clr = 2;
这种写法可以么,毕竟2也是Red的值,应该支持隐式类型转换吧!
嗯…最好不要,C语言可以;Cpp不行,Cpp检查比较严格
那有没有具体一点的应用场景呢?
有,我们稍微回顾一下之前的扫雷
运用了枚举这个技巧后:
关于枚举其实我们后来还会有很多的应用场景,这就需要大家自己去慢慢发现了!
总结
其实中途跳过去写Cpp了,现在再跳回来…
反复横跳!