自定义类型:联合和枚举
上文我们了解到了对于C语言中没有定义的类型,我们可以用结构体自己重新定义一个适用的类型。那么自定义类型除了结构体还有另外两种自定义类型,分别是联合、枚举。
1.联合体类型的声明
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以是不同的类型。但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。
给联合体其中⼀个成员赋值,其他成员的值也跟着变化。
#include <stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = {0};
//计算联合体变量的⼤⼩
printf("%d\n", sizeof(un));
return 0;
}
我们发现计算的结果如下图所示,不难发现联合体的特点是所有成员共⽤同⼀块内存空间,只为联合体中最大的成员分配足够大的空间。
2.联合体的特点
联合的成员是共⽤同⼀块内存空间的,这样⼀个联合变量的⼤⼩,⾄少是最⼤成员的⼤⼩(因为联合⾄少得有能⼒保存最⼤的那个成员)。
我们从下面的代码来讨论联合体的特点吧!
#include <stdio.h>
//联合类型的声明
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;
}
如下图所示,我们发现打印出联合体中成员的地址竟然是一样的,再次证明了联合的成员是共⽤同⼀块内存空间的。
我们来看另一端代码,继续探讨联合体的特点。
#include <stdio.h>
//联合类型的声明
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;
}
通过代码调试我们发现,联合体中对char c的修改是在int i的第一个字节地址处修改,得出结论:联合的成员是共⽤同⼀块内存空间的。
打印结果如下:
下面我运用图解的方式来理解联合体的特点如下:
3. 联合体⼤⼩的计算
• 联合的⼤⼩⾄少是最⼤成员的⼤⼩。
• 当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。
#include <stdio.h>
union Un1
{
char c[5];//最大成员大小为5,最大对齐数为4,5不是4的整数倍,所以Un1的大小为8
int i;
};
union Un2
{
short c[7];//最大成员大小为2*7=14,最大对齐数为4,14不是4的整数倍,所以Un2的大小为16
int i;
};
int main()
{
//下⾯输出的结果是什么?
printf("%d\n", sizeof(union Un1));//8
printf("%d\n", sizeof(union Un2));//16
return 0;
}
打印结果如下图所示:
我们发现联合体的大小计算结果不是最大成员的大小,因为当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。
4.相同结构体和联合体的对比
//结构体
struct S
{
char c;
int i;
};
struct S s = {0};
//联合体
union Un
{
char c;
int i;
};
union Un un = {0};
上面的结构体和联合体内存对比图解:
我们发现联合体会比结构体更加节约内存。
5.联合体的运用
使用联合体是可以节省空间的,举例:
比如,学校有多种课程,
理科生:语文,数学,英语,物理,化学,生物
文科生:语文,数学,英语,政治,历史,地理
职高生:语文,数学,英语,计算机,专业课
那么语文,数学,英语是必选的,是公共属性,我们可以放在结构体中,另外的科目,为特殊属性,我们可以将特殊属性用联合体来存储,节省空间。
struct course
{
//公共属性
int Chinese;//语文成绩
int Maths;//数学成绩
int English;//英语成绩
//特殊属性
int Physics;//物理成绩
int Chemistry;//化学成绩
int Biology;//生物成绩
int Politics;//政治成绩
int History;//历史成绩
int Geography;//地理成绩
int Computer;//计算机成绩
int Professional course;//专业课成绩
};
这样用结构体存储占用内存过多,我们可以用联合体进行改进,如下:
struct course
{
//公共属性
int Chinese;//语文成绩
int Maths;//数学成绩
int English;//英语成绩
//特殊属性
union{
struct
{
int Physics;//物理成绩
int Chemistry;//化学成绩
int Biology;//生物成绩
};
struct
{
int Politics;//政治成绩
int History;//历史成绩
int Geography;//地理成绩
};
struct
{
int Computer;//计算机成绩
int Professional course;//专业课成绩
};
}
};
我们把公共属性单独写出来,剩余属于各种课程成绩本⾝的属性使⽤联合体起来,这样就可以减少所需的内存空间,⼀定程度上节省了内存。
6. 枚举类型的声明
枚举顾名思义就是⼀⼀列举。
把可能的取值⼀⼀列举。
⽐如我们现实⽣活中:
⼀周的星期⼀到星期⽇是有限的7天,可以⼀⼀列举
性别有:男、⼥、保密,也可以⼀⼀列举
⽉份有12个⽉,也可以⼀⼀列举
三原⾊,也是可以一 一列举
这些数据的表⽰就可以使⽤枚举了。
enum Day//
星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex//
性别
{
MALE,
FEMALE,
SECRET
};
以上定义的enum Day ,enum Sex 都是枚举类型。
{}中的内容是枚举类型的可能取值,也叫枚举常量。
这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。
enum Color//
颜⾊
{
RED=2,
GREEN=4,
BLUE=8
};
7. 枚举类型的优点
为什么使⽤枚举?
我们可以使⽤#define 定义常量,为什么⾮要使⽤枚举?
枚举的优点:
- 增加代码的可读性和可维护性
- 和#define定义的标识符⽐较枚举有类型检查,更加严谨
- 便于调试,预处理阶段会删除#define 定义的符号
- 使⽤⽅便,⼀次可以定义多个常量
- 枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤
8. 枚举类型的使⽤
enum Color//
颜⾊
{
RED=1,
GREEN=2,
BLUE=4
enum Color clr = GREEN;//使⽤枚举常量给枚举变量赋值
};