在上一章节中我们详细讲解了C语言中自定义类型中的结构体,而我们将在本章节讲完剩余的联合体和枚举。
目录
1. 联合体
1.1 联合体类型的声明
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。
但是编译器只为最⼤的成员分配足够的内存空间。联合体的特点是所有成员共用同⼀块内存空间。
所以联合体也叫:共用体。
在这里可能有同学会对这个共享同一块内存空间有所疑惑?不同的结构体成员是如何共享同一块空间的呢?让我们一探究竟。
我们通过打印出联合体和里面联合体成员的地址,我们发现,他们的地址都是一摸一样的,也就是说他们是共享同一个首地址,然后不同的联合体成员根据不同字节大小以共同的首地址向后延伸。
所以这也意味着给联合体其中⼀个成员赋值,其他成员的值也跟着变化。
举个栗子:
union test
{
char c;
int i;
}u;
int main()
{
u.i = 0x11443399;
u.c = 0x13;
return 0;
}
在这里我们设置一个联合体,并先将十六进制的常量整形 11443399 赋值给 u.i ,将十六进制的常量整形 13 赋值给 u.c 。
进入调试后我们看到:
在 u.c 赋值完后 u.i 的值也会跟着发生改变。
1.2 联合体类型的大小计算
讲到这里可能会有同学好奇,那我们联合体的大小是如何计算的呢?如果说所有联合体成员共享同一块地址,那是不是联合体大小就是那个最大的结构体成员的大小呢?那带着这些我们进入我们的新part
首先让我们来看一组代码:
union test
{
char c;
int i;
double d;
}u;
int main()
{
printf("%zd", sizeof(u));
return 0;
}
看看打印结果:
哦!最大联合体成员是 double 类型的 d ,d 的大小是 8 , 所以联合体大小就是那个最大的联合体成员的大小。真的是这样吗?让我们再看另一个代码:
union test
{
char c[6];
int i;
}u;
int main()
{
printf("%zd", sizeof(u));
return 0;
}
诶,怎么还是 8 ?最大的联合体成员是 char 类型的数组 c[ 5 ],大小是 6 啊。为什么结果还会是 8 呢?
其实啊
联合体的大小也是遵循最大对齐数原则的(详情见上一章的结构体)
1.3 联合体类型的应用
其实我们发现联合体跟结构体的相比就是节省空间,在符合最大对齐数条件的情况下,联合体的大小确实就是最大联合体成员的大下。他不会像结构体一样会存在过度浪费空间的情况。但联合体的这种节省优势也是建立在牺牲了各个联合成员之间的独立性的前提下。联合体成员的改变会相互影响。那我们的联合体有何应用嗯?
比如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。 每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
- 图书:书名、作者、页数
- 杯子:设计、衬衫
- 设计、可选颜色、可选尺寸
假如此时我们没有接触过联合体,那么我们就大概率是写出下列地代码
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;
};
我们可以自己填写自己想要的数值。
2. 枚举类型
2.1 枚举类型的声明
枚举顾名思义就是⼀⼀列举。
把可能的取值⼀⼀列举。
比如我们现实生活中: ⼀周的星期⼀到星期日是有限的7天,可以⼀⼀列举
- 性别有:男、⼥、保密,也可以⼀⼀列举
- 月份有12个月,也可以⼀⼀列举
- 三原⾊,也是可以意义列举
这些数据的表示就可以使用枚举了。
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,当然在声明枚举类型的时候也可以赋初值。
比如
这里放两个例子大家自行感受下。
2.2 枚举类型的优点
为什么使用枚举?
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 便于调试,预处理阶段会删除 #define 定义的符号
- 使用方便,⼀次可以定义多个常量
- 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
3.总结
总的来说,本章节的内容是比较简短的,能讲的内容不多。联合体和枚举也是属于那种,只有当你写的代码多了,接触多了,才能悟出他们的精妙之处,属于比较灵性的那一档。
那我们本章就到此结束了,老规矩,感谢每一个看到最后的同学,有任何不足请多多包涵并希望您能不吝在评论区中指出。
完