联合体类型和枚举类型

联合体 

联合体类型的声明

像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。 
联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。
所以给联合体其中⼀个成员赋值,其他成员的值也跟着变化。

对于联合体,其语法结构和结构体对比可以说唯一的变换就是把struct换成union,其他的都一模一样,所以我们知道了struct的语法结构相当于知道union的语法结构,其初始化和创建也就得心应手。(上篇文章已经讲了struct的语法结构(声明),这里就不讲了,不知道的可以翻到上篇文章看一下)

并且之前也讲过,对于union也可以进行匿名操作,间接访问操作符和直接访问操作符也可以应用在上面。

对于我们上一篇的位段同理上述行为也可以,并且位段和前面两种类型的语法结构也很相似,就是多了个:和数字,关键字还是struct。

所以这三种类型的语法结构可以说都非常相似,但是其内部成员的内存分配可完全不一样。结构体和位段的内存分配我已经讲过了,下面带来联合体的内存分配

#include 
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = {0};
//计算连个变量的⼤⼩
printf("%d\n", sizeof(un));
return 0;}

联合体类型和枚举类型_第1张图片

 为什么为4,下面给大家说下其内存分配就能理解了

联合体内存分配 

 联合体内存分配也符合对齐规则。

联合体的大小至少是最大成员的大小

当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。

(有些人会认为联合体的大小就是最大成员的大小,但其还会发生对齐,所以是至少是最大成员的大小,如下举几个例子去证明联合体大小至少是最大成员的大小)

#include 
union Un1
{
 char c[5];
 int i;
};
union Un2
{
 short c[7];
 int i;
};
int main()
{
 //下⾯输出的结果是什么?
 printf("%d\n", sizeof(union Un1));
 printf("%d\n", sizeof(union Un2));
 return 0;
}

数组的对齐数是默认对齐数和其成员类型大小的较小值,像char c[5]对齐数为1.所以分析得出结果为8,16.。验证得知正确。(说了以上结论后想必推导出结果很简单,就不说其结果的推导)

联合体类型和枚举类型_第2张图片

而其最大成员大小分别为 5和14,但是其联合体大小为 8 和16.

所以联合体大小至少是最大成员的大小,且当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。

 对于联合体的各个成员都是从联合体的首个字节开始进行存储(像联合体这种包含多个数据的类型内部都是从低地址到高地址进行存储,数组,结构体这种包含多个数据的也是同理)。从而会共用同一块内存空间。

下面有一段代码去支持以上结论

#include 
//联合类型的声明
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;
}

 联合体类型和枚举类型_第3张图片

足以说明每个成员都是从联合体的首个字节开始进行存储的,从而会有共用 。导致它们之间有关联,改变其中一个值,可能会导致其他值发生变化。

 所以这就是联合体的内存分配。

 相同成员的结构体和联合体对比

 我们再对⽐⼀下相同成员的结构体和联合体的内存布局情况

struct S
{
 char c;
 int i;
};
struct S s = {0};
union Un
{ 
char c;
int  i;
};
union Un un = {0};

联合体类型和枚举类型_第4张图片

所以这就是其内存的对比图

 联合体可以节省空间(优点)

 我们使⽤联合体是可以节省空间的,

⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。
每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、⻚数
杯⼦:设计
衬衫:设计、可选颜⾊、可选尺⼨
那我们不耐心思考的话可以直接写出⼀下结构:  
​
struct gift_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;//尺⼨
};
​
上述的结构其实设计的很简单,⽤起来也⽅便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的⼤⼩就会偏⼤,⽐较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常⽤的。⽐如:
商品是图书,就不需要design、colors、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;
};

 这样的代码所创造出的struct空间大小因为内含union的缘故,所以不通用的属性是共用一块内存,而之前的代码是不存在共用的,都是独立的。

所以优化后的版本在能达到同样的效果时且其空间更小,更节省内存。

用联合体的方式判断大小端存储模式 

int check_sys()
{
 union
 {
 int i;
 char c;
 }un;
 un.i = 1;
 return un.c;//返回1是⼩端,返回0是⼤端
}

 这里就不过多叙述了,很简单的一题,只要学过数据在内存中的存储的就都知道其具体机制。

枚举 

 枚举类型的声明

enum Day//星期
{
 Mon,
 Tues,
 Wed,
 Thur,
 Fri,
 Sat,
 Sun
};
enum Sex//性别
{
 MALE,FEMALE,
 SECRET
};
enum Color//颜⾊
{
 RED,
 GREEN,
 BLUE
};

枚举跟前面的结构体和联合体完全不同。差别很大。

以上定义的 enum Day enum Sex enum Color 都是枚举类型。

{}中的内容是枚举类型变量的可能取值,也叫枚举常量(注意这是常量) 。 这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。赋完初值后它的下一个枚举常量的值则是以上一个枚举常量的值为基础加一的。(因为是常量,在赋完初值后不可再改变其值)

enum Color//颜⾊
{
 RED=2,
 GREEN=4,
 BLUE=8
};

下面这图证明了 在枚举常量赋完初值后它的下一个枚举常量的值则是以上一个枚举常量的值为基础加一的。

 联合体类型和枚举类型_第5张图片

注意{}里面的是,不是;    最后的枚举常量后面的,有没有都可以。

枚举类型的使用 

enum Color//颜⾊
{
 RED=1,
 GREEN=2,
 BLUE=4
};
enum Color clr = GREEN;//使⽤枚举常量给枚举变量赋值

我们用sizeof对其测试得出一系列结论

联合体类型和枚举类型_第6张图片一系列枚举类型如enum  color等等本质其实是int 类型 ,因为其类型大小为4个字节。

而对于枚举常量类型为enum color  ,其类型大小也为4个字节

所以对于创建的clr变量我们将其赋值是用枚举常量给其赋值。

不能用整数将其赋值,如4,5,6等。因为其是int类型,而这个是enum color类型。

而有些人在c语言中可以实现用4,5,6等整形将其赋值给枚举变量的情况,这是因为c语言不够严谨,而其枚举类型本质上其实还是int类型,所以就让它过了。 如果换做c++,其类型检查比较严格,所以即使其枚举类型本质是int类型,但其表面还是枚举类型,严格的c++就会导致不能用整形赋值到枚举变量上去。

所以对于枚举变量我们为了严谨性还是只能用枚举常量对其进行赋值(只能选对应的枚举类型{}里面的枚举常量)

当然对于枚举常量我们不仅能给枚举变量赋值,还能单拿出来自己用在别的地方 (它们是已经定义好的常量,当然能自己单独用)

联合体类型和枚举类型_第7张图片

枚举依然能进行匿名操作。枚举不能用直接访问操作符和间接访问操作符。枚举依然能像结构体和联合体一样在;前面创建变量(创建类型的同时创建变量) 

这大概就是枚举的主要知识点,当然它还有更多的细节,对于这些细节我就不讲了(实在有点多),这里有篇好文我推荐下,大家可以去看下C语言枚举类型enum(全面详细直观)_enum c语言-CSDN博客

讲的枚举知识点比我详细的多,(我这只是讲一下主要的知识点,没更深涉及了,你应用枚举基本上也就是应用到我这上面讲的知识点)

 枚举的优点

为什么使⽤枚举?
我们可以使⽤ #define 定义常量,为什么⾮要使⽤枚举?
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符⽐较枚举有类型检查,更加严谨。
3. 便于调试,预处理阶段会删除 #define 定义的符号
4. 使⽤⽅便,⼀次可以定义多个常量
5. 枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤。(也能声明在函数外,此时就能在全局使用)。而#define定义的常量都是在全局使用的,不会被限制到只能在函数内使用。

总结 

所以目前我们就讲解完了联合体和枚举  。 下一篇将给大家讲解动态内存管理。

谢谢大家!!!

你可能感兴趣的:(c语言知识点专栏,c)