目录
- 1. 联合体
- 1.1 联合体类型的声明
- 1.2 联合体变量的创建
- 1.3 联合体的特点
- 1.4 联合体在内存中的存储
- 1.5 联合体使用举例
- 2. 枚举类型
- 2.1 枚举类型的声明
- 2.2 枚举变量的创建和初始化
- 2.3 枚举类型的大小
- 2.4 枚举类型的优点
正文开始
上次我们通过《C语言 结构体详解》学习了结构体的相关知识,今天我们来学习一下结构体的亲戚联合和枚举,它们同结构体一样,都是C语言中的自定义类型,由一个或多个成员构成。因为本文所讲内容与结构体有很多相似之处,所以先学习结构体再食用本文更佳呦~
1. 联合体
1.1 联合体类型的声明
联合体的声明和结构体的声明类似:
union Un_name
{
member-list;
};
其中:
- union用来声明这是一个联合体
- Un_name是联合体名
- member-list为成员列表
1.2 联合体变量的创建
#include <stdio.h>
//联合体类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = { 0 };
un.i = 3;
un.c = 'a';
return 0;
}
联合体变量的创建以及初始化等与结构体基本相同,这里不再赘述。
1.3 联合体的特点
联合体的声明与结构体基本相同,那两者有什么区别呢?我们都知道结构体在内存中的存储符合内存对齐规则,这样做的好处就是增强了性能,但是却浪费了空间。而联合体主打一个节省空间,原因如下:
- 联合体成员共用一块内存(所以联合体又叫共用体)
- 联合体大小至少为最大成员的大小,这样才能确保能放下所有数据
我们写个代码验证一下:
# include <stdio.h>
union Un
{
char c;
int i;
};
int main()
{
union Un un = { 0 };
printf("%p\n", &(un.c));
printf("%p\n", &(un.i));
printf("%p\n", &un);
return 0;
}
运行结果:
由此我们验证了,结构体成员共用一块内存。
1.4 联合体在内存中的存储
联合体成员共用一块内存,那他们究竟是怎么存储的呢?
- 联合体的大小至少是最大成员的大小
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
- 由于联合体成员共用同一块内存,所以一般独立使用各个联合体成员
例如:
#include <stdio.h>
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
int main()
{
printf("Un1 -> %zd\n", sizeof(union Un1));
printf("Un2 -> %zd\n", sizeof(union Un2));
return 0;
}
在内存中的存储情况如下:
上图可以看出,整个联合体的大小满足两个条件:
- 至少是最大成员的大小
- 大小要对齐到最大对齐数的整数倍。例如 Un1 最大的对齐数是4,所以总大小补齐为8;Un2 最大的对齐数是4,所以总大小补齐为16
运行验证一下:
1.5 联合体使用举例
假如我们需要记录图书、杯子、衬衫三种商品的数据,每一种商品都有:库存量、价格、商品类型和一些其他信息:
- 图书:书名、作者、页数
- 杯子:设计
- 衬衫:设计、颜色、尺寸
如果我们使用结构体书写:
struct Product
{
//共有属性
int stock_number;//库存量
double price;//价格
int item_type;//商品类型
//特有属性
char title[20];//书名
char author[20];//作者
int num_page;//页数
char design[30];//设计
int colors;//颜色
int sizes;//尺寸
};
这样写似乎没有什么问题,但一个结构中包含了所有商品的各种属性,当我们描述图书的时候,就用不上设计、颜色和设计,这样就使得结构体的大小偏大,比较浪费内存。所以我们可以使用联合体书写:
struct Product
{
//共有属性
int stock_number;//库存量
double price;//价格
int item_type;//商品类型
union
{
struct
{
char title[20];//书名
char author[20];//作者
int num_page;//页数
}book;//图书
struct
{
char design[30];//设计
}mug;//杯子
struct
{
char design[30];//设计
int colors;//颜色
int sizes;//尺寸
}shirt;//衬衫
}item;
};
这样就相当于把三种商品的特有属性共存在一块内存中,使用时也相互独立,这样保证了正常使用,也能够节省空间。
2. 枚举类型
枚举,顾名思义就是一个一个列举,比如我们现实生活中:
- 星期一到定期日可以列举
- 性别可以列举
- 月份可以列举
2.1 枚举类型的声明
枚举类型的声明与结构体类似:
enum Enum_name
{
value1,
value2,
value3,
//...
};
- 使用关键字enum来声明枚举类型
- Enum_name为枚举名
- {}中的内容为枚举类型的可能取值,称作枚举常量。
- 枚举常量之间由,隔开
- 枚举常量都是有初始值的,默认从0开始,步长为1依次递增。
- 给定枚举常量的值,就类似于#define定义标识符,只不过#define是在预处理阶段完成的,枚举类型是在编译阶段完成的
例如:
enum Color
{
RED,//0
GREEN,//1
BLUE//2
};
我们也在声明枚举类型时赋值:
enum Color1
{
RED=3,//3
GREEN,//4
BLUE//5
};
enum Color2
{
RED=3,//3
GREEN=5,//5
BLUE=7//7
};
enum Color3
{
RED,//0
GREEN=3,//3
BLUE//4
};
对于枚举类型的理解:
声明一个枚举,就是定义了一种类型,这种类型的可能取值就是枚举常量的值
2.2 枚举变量的创建和初始化
枚举变量的创建和初始化与结构体类似,这里只做演示,详情不再赘述。
enum Color
{
RED,//0
GREEN,//1
BLUE//2
};
int main()
{
enum Color red = 0;//等同于RED
//定义了一个类型为 enum Color 的枚举变量red
//变量 red 的值为0,也就是RED
return 0;
}
2.3 枚举类型的大小
枚举是一种数据类型,它的可能取值就使枚举常量的值,枚举常量本质都是整型,也就是说,不管是枚举类型,还是枚举变量,亦或是枚举常量,它们的大小都与整型相同,都是4个字节,下面我们验证一下:
#include <stdio.h>
enum Color
{
RED,
GREEN,
BLUE
};
int main()
{
enum Color red;
printf("%d\n", sizeof(enum Color));//枚举类型
printf("%d\n", sizeof(RED));//枚举常量
printf("%d\n", sizeof(GREEN));//枚举常量
printf("%d\n", sizeof(BLUE));//枚举常量
printf("%d\n", sizeof(red));//枚举变量
return 0;
}
运行结果:
2.4 枚举类型的优点
有的人可能会想到,既然都是定义常量,那为什么不用#define呢?
因为枚举有以下优点:
- 枚举将标识符集中起来,不像是#define那样分散开,增加了代码的可读性和可维护性
- 枚举类型定义的标识符有类型检查(是枚举类型),更为严谨,而#define没有(直接替换)
- 便与调试,预处理阶段会删除#define定义的符号
- 使用方便,一次可以定义多个常量
- 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用,而#define定义的标识符是全部都直接替换
完