目录
我们刚学完了结构体,相信大家对自定义类型也有了些许了解,但是自定义类型中还有两个我们尚未学习,它们也急不可耐的想展现在我们面前了,所以接下来就由姜糖来带大家认识一下吧!
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。 但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所 以联合体也叫:共用体。
所以联合体的声明和结构体类似,结构体的关键词是struct,而联合体的关键词是union。
- union Un
- {
- char a;
- int b;
- };
接下来我们再来探讨一下联合体的存储大小,我们用代码测试一下上面定义的联合体:
- #include
-
- union Un
- {
- char a;
- int b;
- }un;
-
- int main()
- {
- printf("%zd", sizeof(un));
-
- return 0;
- }
打印结果为:

而如果此为结构体,考考大家前面学过的知识,应该为多少呢? ·(答案为8)
那么我们发现,联合体和结构体的存储模式是不一样的。那么联合体是怎么存储的呢?
联合体的存储规则:
- 联合体的大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
首先我们来了解第一句话, 正如我们前面讲到的那样,联合体的特点是所有成员共用同⼀块内存空间,所以联合体大小至少是最大成员的大小,不然当我们用到最大成员时,联合体就存储不下了。
那么接下来我们用代码看看联合体的成员是否是共用一块内存空间:
- #include
-
- union Un
- {
- char a;
- int b;
- }un;
-
-
- int main()
- {
- printf("%p\n", &un); //联合体的地址
- printf("%p\n", &(un.a));//联合体中a成员的地址
- printf("%p\n", &(un.b));//联合体中b成员的地址
-
- return 0;
- }
打印结果如下:

我们发现它们的地址是一样的,我们可以确认它们都为应该地址,为了更加确定这个观点,我们来进一步测试:
- #include
-
- union Un
- {
- char a;
- int b;
- }un;
-
-
- int main()
- {
- un.b = 0x11223344;
- un.a = 0x66;
- printf("%x", un.b);//看看a的改变是否影响b
- return 0;
- }
打印结果如下:

所以可以更一步的确定它们共用一块内存,所以这个联合体的图为:

所以我们发现联合体比结构体更节约存储空间。
而第二句话我们该怎么理解呢?
我们用代码具体分析,比如:
- union Un
- {
- char a[6];
- int b;
- }un;
-
- int main()
- {
- printf("%zd", sizeof(un));
-
- return 0;
- }
那么之里面的最大成员为char a[6],那么大小就为6了吗,真的是这样子的吗?那我们来看打印结果:

结果不为6而为8 ,而这里和结构体一样要对齐

所以最后6应该变成4的倍数8。
我们也发现,其实联合体深入节约存储空间但也会存在内存浪费的现象。
那么我们学习了联合体,那联合体有什么实际运用呢?
大家在前面的学习中也发现了联合体的节约空间的性质,那么实际运用中也和节约空间密不可分,比如生活中常见的事务的分类,那我们拿书、手机、手表来举例。
首先书、手机、手表都是一个大类——售卖的商品
手机:价格、品牌
书:价格、作者、书名
手表:价格、品牌、工艺
那么它们的结构体该怎么写呢?
- struct commodity
- {
- //公共属性
- int price; //价格
- //私有属性
- char brand[10];//品牌
- char author[10];//作者
- char title[10];//书名
- char workmanship[10];//工艺
- };
我们发现这里有很多私有属性,会被大量浪费。当这些私有属性共用一块内存空间时是不是可以大大节约空间的浪费所以接下来,我们用union联合体来优化代码:
- struct commodity
- {
- int price; //价格
- union
- {
- struct
- {
- char brand[10];//品牌
- }phone;
-
- struct
- {
- char author[10];//作者
- char title[10];//书名
- }book;
-
- struct
- {
- char workmanship[10];//工艺
- char brand[10];//品牌
- }Wristwatch;
- }item;
- };
这样设计结构体就能大大减少空间的浪费。
在学习的过程中我们也发现联合体非常适合去测试大小端的字节序,因为前面我们在测试大小端的时候我们是将同一个内存强制类型转换的,而这里我们用联合体将两个类型保存在同一个空间中就行了,代码如下:
- #include
-
- union Un
- {
- int a;
- char b;
- };
-
- int main()
- {
- union Un un = { 0 };
- un.a = 1;
- if (un.b == 1)
- {
- printf("为小端");
- }
- else
- printf("为大端");
-
- return 0;
- }
枚举顾名思义就是⼀⼀列举。 把可能的取值⼀⼀列举。
而我们生活中有很多可以一一列举的事务,如星期,月份,三原色等等,比如三原色的枚举:
- emun Threeprimarycolors
- {
- red;
- blue;
- green;
- };
{}中的内容是枚举类型的可能取值,也叫 枚举常量。这些可能取值都是有值的,默认从0开始,依次递增1:
比如这里的red就为0,blue就为1,green就为2。
当然在声明枚举类型的时候也可以赋初值:
- emun Threeprimarycolors
- {
- red;
- blue = 6;
- green;
- };
这样就blue就变成了6,前面没有赋值的就按照原来的从0开始,red为0,赋值后面的则继续加一,green就为7.
我们发现枚举和#define定义非常相像,那么我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
1. 增加代码的可读性和可维护性。
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 便于调试,预处理阶段会删除 #define 定义的符号。
4. 使用方便,⼀次可以定义多个常量
5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用。
C语言的内容不止于此,还有更多需要我们共同发现,今天C语言的内存数据管理就到这里啦。如果姜糖有讲的不好的地方欢迎大家提出,谢谢大家,也希望大家能一键三连哦!