目录
[1. 联合体](#1. 联合体)
[1.1 联合体类型的声明](#1.1 联合体类型的声明)
[1.2 联合体的特点](#1.2 联合体的特点)
[1.3 相同成员的结构体和联合体对比](#1.3 相同成员的结构体和联合体对比)
[1.4 联合体大小的计算](#1.4 联合体大小的计算)
[1.5 联合的一个练习](#1.5 联合的一个练习)
[2. 枚举类型](#2. 枚举类型)
[2.1 枚举类型的声明](#2.1 枚举类型的声明)
[2.2 枚举类型的优点](#2.2 枚举类型的优点)
[2.3 枚举类型的使用](#2.3 枚举类型的使用)
1. 联合体
1.1 联合体类型的声明
联合体所有成员共享同一块内存,所以联合体也称:共用体。
编译器只为最大的成员分配足够的内存空间,但由于内存对齐要求,联合体的实际大小可能大于最
大成员的大小。
给联合体其中一个成员赋值,其他成员的值也会跟着变化。
char c:1 字节int i:4 字节 最大成员是int,基础需要至少 4 字节。

为什么是4呢?
1.2 联合体的特点
联合的成员是共用同⼀块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联
合至少得有能力保存最大的那个成员),但是受内存对齐 的影响,联合体的实际大小可能大于最大
成员的大小。

联合体所有成员共享同一块内存起始地址:
&un (联合体起始地址) = &un.i = &un.c
结构体各个成员地址依次递增;联合体所有成员起始地址完全一致,这是最核心区分特征。
如果将一个变量赋值或者修改其中一个变量,会改变共享这块内存的内容,其他变量的内容读取
到的结果也可能受到影响。

可以发现:
联合体成员共用同一块内存,修改成员c会覆盖共用内存的一部分,因此再读取成员i时,得到的值
也会受到影响。
内存分布如下:

前提:常见 x86 小端平台(低字节存低地址)
un.i = 0x11223344内存排布: 地址 0:0x44地址 1:0x33地址 2:0x22地址 3:0x11un.c = 0x55: 修改起始第 0 字节 ,地址 0 被覆盖为0x55,最终内存:0x55 0x33 0x22 0x11,打印结果:11223355
1.3 相同成员的结构体和联合体对比
// 结构体
struct S
{
char c;
int i;
};
// 联合体
union Un
{
char c;
int i;
};
1. 内存大小(32/64 位主流环境)
sizeof(struct S)= 8 字节char c:1 字节;存在 3 字节内存对齐填充(粉色浪费区域)int i:4 字节,必须从 4 的倍数地址开始存放
sizeof(union Un)= 4 字节- 分配空间 = 最大成员大小(
int占 4 字节) - 所有成员共享同一片内存,无独立空间
- 分配空间 = 最大成员大小(

2. 内存布局核心区别
结构体 struct S
✅ 成员独占内存,地址依次往后排列
&s.c ≠ &s.i,两个成员地址不同,互不覆盖。
联合体 union Un
✅ 所有成员共享同一个起始地址,重叠占用内存
&un.c == &un.i == &un
⚠️ 赋值冲突:修改**
un.c** 会覆盖**un.i** 最低字节;修改**un.i** 会修改**un.c**的值。
3. 核心特性对照表

💡 典型使用场景
- 结构体:用来描述一组同时存在的属性(学生:姓名、年龄、分数)
- 联合体:节省内存;大小端检测;协议解析(同一内存用多种类型解析)
1.4 联合体大小的计算
规则:
1. 联合体的大小至少是最大成员的大小;
2. 当最大成员的大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。
#include <stdio.h>
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;
}
1. union Un1
char c[5]→ 5 字节int i→ 4 字节 最大成员字节数:5 向上补齐到 4 的倍数:8sizeof(union Un1) = 8
2. union Un2
short c[7]:short 占 2 字节,7×2 = 14 字节int i:4 字节 最大成员字节数:14 向上补齐到 4 的倍数:16sizeof(union Un2) = 16
使用联合体是可以节省空间的,举例:
比如,我们要搞一个活动,要上线一个礼品兑换单,礼品兑换单中有三种商品:图书、杯子、衬
衫。
每一种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、页数
杯子:设计
衬衫:设计、可选颜色、可选尺寸
一般会这样写:
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;
};
1.5 联合的一个练习
写一个程序,判断当前机器是大端?还是小端?
原理讲解
- 大小端概念
-
小端模式 :数据低字节 存放在内存低地址(PC、x86 架构默认)
-
大端模式 :数据高字节 存放在内存低地址(网络传输常用)
-
联合体核心特性 联合体所有成员共享同一块内存空间。
int i和char c起始地址完全相同。 -
执行过程
un.i = 1;二进制:00000000 00000000 00000000 00000001
-
小端 :低地址存放**
00000001,un.c** 读到1,函数返回1 -
大端 :低地址存放
00000000,un.c读到0,函数返回0

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
-
声明枚举时,可以手动指定初始值
enum Color//颜色
{
RED=2,
GREEN=4,
BLUE=8
};
补充知识点
- 枚举常量本质是整型常量
- 如果只给某一个常量赋值,后面未赋值的常量在前一个数值基础上 + 1
- 示例:
enum Color
{
RED=5,
GREEN, // 6
BLUE // 7
};
2.2 枚举类型的优点
思考问题
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举五大优点
-
增加代码的可读性和可维护性
-
和**
#define** 定义的标识符对比,枚举具备类型检查,代码更加严谨。 -
便于调试 :预处理阶段编译器会删除**
#define**定义的符号,调试器看不到宏名;枚举常量符号可以在调试时直接查看。 -
使用方便,一次可以定义多个常量。
-
枚举常量遵循作用域规则 :如果枚举声明写在函数内部,枚举常量只能在该函数内使用;而**
#define**作用域默认从定义处直到文件末尾。
2.3 枚举类型的使用
enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};
enum Color clr = GREEN;//使用枚举常量给枚举变量赋值
重要特性:整型向枚举变量赋值的区别
1. C语言:
允许直接用整数给枚举变量赋值,编译器通常只会产生警告,而不会报错。
enum Color clr = 2; // C语言合法
本质 C 语言中枚举常量底层是 int,类型检查宽松。
2. C++:
类型检查更加严格,不允许直接将字面整数赋值给枚举变量,直接写会编译报错。 如果一定要转
换,必须显式强制类型转换:
enum Color clr = (enum Color)2;