union 共用体¶
方法速览¶
| 任务 | 方法 | 口诀 |
|---|---|---|
| 判断行为 | 所有成员共享同一块内存,同一时刻只有一个成员有效 | 全家住一间房 |
| 求大小 | \(\max\)(各成员大小),再对齐到最大成员的对齐值 | 取最长那根 |
| 访问成员 | 变量用 .,指针用 -> |
变量点、指针箭 |
| 与 struct 区别 | struct 人人一间房(大小 \(\geq\) 成员和),union 全员一间房(大小 = 最大成员) | struct 合住、union 拼租 |
定义与内存布局¶
union(共用体)是让多个成员共用同一块内存的构造类型:
内存布局对照(以 4 字节 union 为例):
| 成员 | 占用的字节 |
|---|---|
num |
全部 4 字节 |
value |
全部 4 字节 |
symbol |
与 num 的第 1 个字节重叠 |
三条性质:
- 大小 = 最大成员(再按最大对齐值补齐):
sizeof(Data) = 4,不是 \(4+4+1\); - 同一时刻只有一个成员有效:给
value赋值后,num里读到的是 value 的位串重解释,是垃圾值; - 改一个成员,其余成员跟着变化——因为动的是同一块内存。
例:data.num = 1; data.symbol = 'A'; 之后 data.num 不再是 1——symbol 只覆盖了低 1 个字节,读出来是乱码。
成员访问¶
与 struct 完全一样:变量用 .,指针用 ->。
初始化:不带指示符时只能初始化第一个成员:union Data d = {42}; 给的是 num。C++11 起可用 union Data d = {.value = 3.14}; 指定成员。
与 struct 的区别¶
| 对比项 | struct |
union |
|---|---|---|
| 内存 | 每个成员各占一块 | 所有成员共享一块 |
| 大小 | \(\geq\) 成员大小之和(对齐填充) | = 最大成员(对齐补齐) |
| 有效成员 | 全部同时有效 | 同一时刻只有一个 |
| 用途 | 把不同类型打包在一起(点坐标) | 同一块内存的多种解读(省空间/拆字节) |
例:
union 的两个经典用途:
- 拆字节:把
float与char[4]放进 union,写 float 读 char 数组,逐字节取出; - 判大小端:
u.num = 1; u.byte[0] == 1说明低字节放在低地址(小端)。
练习题目¶
练习 1 · 成员访问(变量)¶
题目
union Data { int num; float value; char symbol; }; union Data data; 要修改 value 成员存 3.14,正确写法是( )
- A.
data.value = 3.14; - B.
value.data = 3.14; - C.
data->value = 3.14; - D.
value->data = 3.14;
答案与解析
答案:A
data 是变量,用 .;data->value 是指针的写法,data 不是指针。
干扰项思路:B/D 把成员名与变量名写反;C 混淆了 . 与 ->。
练习 2 · 成员访问(指针)¶
题目
union Data* p; 要通过 p 修改 num 成员,正确写法是( )
- A.
p.num = 5; - B.
*p.num = 5; - C.
num->p = 5; - D.
p->num = 5;
答案与解析
答案:D
指针用 ->;p.num 语法错误(p 不是结构体变量);*p.num 因优先级解析为 *(p.num),同样错误。
练习 3 · 大小 = 最大成员¶
题目
union U { char c; int i; }; 则 sizeof(U) 是( )
- A. \(1\)
- B. \(5\)
- C. \(4\)
- D. \(8\)
答案与解析
答案:C
union 大小 = 最大成员 = int 的 4 字节。
干扰项思路:B 想成了 struct 的“成员相加”。
练习 4 · 大小与对齐¶
题目
union U { char c[9]; int i; }; 则 sizeof(U) 是( )
- A. \(9\)
- B. \(12\)
- C. \(13\)
- D. \(4\)
答案与解析
答案:B
最大成员 char c[9] 占 9 字节,但对齐值是 int 的 4 → 补齐到 4 的倍数 = 12。
干扰项思路:A 忘了对齐补齐。
练习 5 · union 含 double¶
题目
union U { char c; int i; double d; }; 则 sizeof(U) 是( )
- A. \(13\)
- B. \(16\)
- C. \(8\)
- D. \(4\)
答案与解析
答案:C
最大成员 double = 8 字节,char、int 都比它小 → 8。
干扰项思路:A 成员相加;B 与 struct 的对齐算法混淆。
练习 6 · struct 与 union 大小对比¶
题目
struct S { int i; double d; }; 与 union U { int i; double d; }; 的 sizeof 分别是( )
- A. \(16\) 和 \(8\)
- B. \(12\) 和 \(8\)
- C. \(8\) 和 \(8\)
- D. \(16\) 和 \(12\)
答案与解析
答案:A
struct:\(4 + 4\)(对齐填充)\(+ 8 = 16\);union:\(\max(4,8) = 8\)。
练习 7 · struct 相加、union 取最大¶
题目
struct S { char a; int b; }; 与 union U { char a; int b; }; 的 sizeof 分别是( )
- A. \(5\) 和 \(4\)
- B. \(8\) 和 \(4\)
- C. \(8\) 和 \(5\)
- D. \(4\) 和 \(4\)
答案与解析
答案:B
struct:char 后填充 3 字节对齐 int,共 8;union:\(\max(1,4) = 4\)。
干扰项思路:A 是“相加 vs 取最大”的裸算,忘了对齐。
练习 8 · 共享内存的直接后果¶
题目
union U { int i; char c; } u; u.i = 0x41424344; u.c = 'A'; 之后 u.i 的值(小端机)是( )
- A. 不变,仍为 \(0x41424344\)
- B. 未定义,程序崩溃
- C. 变为 \(0\)
- D. 低字节被覆盖,变为 \(0x41424341\)
答案与解析
答案:D
c 与 i 的低字节重叠:写入 'A'(\(0x41\))只改低 8 位,高 3 字节保留。
干扰项思路:A 忽略“共享内存”;这是 union 最核心的行为。
练习 9 · 同一时刻只有一个有效¶
题目
关于 union 成员的有效性,正确的是( )
- A. 所有成员始终同时有效
- B. 同一时刻只有一个成员的值是有效写入的,其余是同一位串的别的解读
- C. 只有第一个成员有效
- D. 只有最后一个定义的成员有效
答案与解析
答案:B
最后一次写入哪个成员,哪个才有效;读其他成员得到的是同一位串按别的类型解读,通常无意义。
练习 10 · 写 A 读 B¶
题目
union U { int i; float f; } u; u.f = 1.0f; cout << u.i; 的输出是( )
- A. \(1\)
- B. \(0\)
- C. \(1065353216\)
- D. 编译错误
答案与解析
答案:C
1.0f 的 IEEE 754 位串是 \(0x3F800000\),按 int 解读就是 \(1065353216\)——写 float 读 int,得到的是位串重解释,不是数值转换。
练习 11 · 用途:判大小端¶
题目
union U { int i; char c[4]; } u; u.i = 1; 若 u.c[0] == 1,说明机器是( )
- A. 小端序
- B. 大端序
- C. 与字节序无关
- D. 16 位机
答案与解析
答案:A
c[0] 对应最低地址的字节;低地址存着 1(低字节)→ 小端。
练习 12 · 用途:拆 float 字节¶
题目
要逐字节查看一个 float 的存储位串,最合适的工具是( )
- A. 把 float 强转成 int
- B.
union { float f; unsigned char b[4]; } - C. 用
strlen - D. 用
sizeof
答案与解析
答案:B
union 写 float、读字节数组,同一位串两种解读——强转会改变数值(截断),拿不到原始位。
练习 13 · 什么时候选 union¶
题目
以下场景最适合用 union 的是( )
- A. 存一个点的横纵坐标 \(x, y\)
- B. 给函数传多个输出参数
- C. 存全班同学的成绩列表
- D. 一种数据包有多种格式,任意时刻只会是其中一种,想省内存
答案与解析
答案:D
互斥使用的多格式共享一块内存是 union 的本职;A、C 要同时保存多个值(B 是传参场景,用引用/指针更合适)。
练习 14 · 什么时候不能用 union¶
题目
以下场景不能用 union 的是( )
- A. 判断机器字节序
- B. 把 float 的 4 个字节逐个取出
- C. 同时保存学生的学号(int)和年龄(int)
- D. 同一块内存按需解释成 int 或 unsigned int
答案与解析
答案:C
学号和年龄要同时有效,union 同一时刻只能保一个。
练习 15 · 初始化¶
题目
union U { int i; char c; }; 执行 U u = {65}; 后( )
- A.
u.i为 65,是当前有效成员 - B.
u.c为 'A',是当前有效成员 - C. 两者同时有效
- D. 编译错误
答案与解析
答案:A
花括号初始化只初始化第一个成员。
干扰项思路:B 位串上 c 确实读到 65,但初始化语义针对的是第一个成员 i。
练习 16 · union 作为 struct 成员¶
题目
struct S { int tag; union { int i; float f; } u; }; 关于 S 的说法正确的是( )
- A. 语法错误,union 不能放进 struct
- B. 合法,u 的大小是 4 字节
- C. 合法,u 的大小是 8 字节
- D. S 的大小等于 tag 与两个 union 成员之和
答案与解析
答案:B
union 可作 struct 成员;内部 union \(\max(4,4) = 4\) 字节,S 共 \(4 + 4 = 8\) 字节。
练习 17 · 匿名 union¶
题目
struct S { int tag; union { int i; float f; }; };(union 无名,s 是 S 的变量)则访问其中的 int 成员写作( )
- A.
s.u.i - B.
s::i - C.
s.i - D.
union.s.i
答案与解析
答案:C
匿名 union 的成员直接提升到外层作用域:s.i、s.f 直接访问。
练习 18 · union 里放数组¶
题目
union U { short s[4]; int i; }; 则 sizeof(U) 是( )
- A. \(4\)
- B. \(2\)
- C. \(12\)
- D. \(8\)
答案与解析
答案:D
short s[4] 占 \(4\times2 = 8\) 字节 > int 的 4 → 8。
练习 19 · typedef union¶
题目
typedef union { int i; double d; } Data; 之后定义变量应写( )
- A.
union Data d; - B.
Data d; - C.
struct Data d; - D.
Data union d;
答案与解析
答案:B
typedef 后 Data 就是类型名,直接用;union Data 反而错误(typedef 出来的名字前不再加 union 关键字)。
练习 20 · union 的类型身份¶
题目
关于 union 的说法,错误的是( )
- A. union 是构造类型,不是基本数据类型
- B. union 的成员默认访问权限是 public
- C. 同类型 union 变量之间可以直接赋值
- D. union 的大小等于所有成员大小之和
答案与解析
答案:D
union 大小 = 最大成员(对齐后)——成员之和是 struct 的算法。
练习 21 · union 赋值¶
题目
Data a, b; a.num = 7; b = a; 之后 b.num 的值( )
- A. \(7\)
- B. \(0\)
- C. 未定义
- D. 编译错误
答案与解析
答案:A
同类型 union 变量可整体赋值,整块内存拷贝——当前有效成员跟着过去。
练习 22 · 修改一个成员(半字节级)¶
题目
union U { int i; short s; } u; u.i = 0xAAAABBBB; u.s = 0; 之后 u.i 是(小端机)( )
- A. \(0xAAAA0000\)
- B. \(0xAAAABBBB\)
- C. \(0x0000BBBB\)
- D. \(0\)
答案与解析
答案:A
s 与 i 的低 16 位重叠:写入 0 只清低半,高半 \(0xAAAA\) 保留 → \(0xAAAA0000\)。
干扰项思路:B 忽略重叠;D 多清了高半;C 清错了一半。
练习 23 · 三成员大小¶
题目
union U { char c; short s; int i; }; 在 64 位环境下 sizeof(U) 是( )
- A. \(1\)
- B. \(2\)
- C. \(4\)
- D. \(7\)
答案与解析
答案:C
\(\max(1, 2, 4) = 4\)。
干扰项思路:D 成员相加。
练习 24 · 与 struct 的语义差¶
题目
同样写 x.i = 1; x.d = 2.0;(i 是 int,d 是 double),对 struct 变量与 union 变量,结果分别是( )
- A. struct:两者都有效;union:只有 d 有效,i 被破坏
- B. 都有效
- C. 都只剩一个有效
- D. union 里 i 仍然有效
答案与解析
答案:A
struct 各住各的房间互不干扰;union 一间房,后写的破坏先写的。
练习 25 · 综合辨析¶
题目
下列说法正确的是( )
- A. union 变量既可以用
.也可以用->访问成员 - B.
sizeof(union)不可能小于其中任何一个成员 - C. union 的所有成员可以同时有效
- D. union 的大小由第一个成员决定
答案与解析
答案:B
union 至少要装下最大的成员:sizeof ≥ 每个成员的大小。A:. 给变量、-> 给指针;C:只有一个有效;D:由最大成员决定。
练习 26 · 空间节省估算¶
题目
某记录要存“要么一个 int 编号、要么一个 8 字节坐标”,共 1 万条。用 union(8 字节)比用 struct(int + double,对齐后 16 字节)大约节省( )
- A. \(40\) KB
- B. \(80\) KB
- C. \(160\) KB
- D. 不节省
答案与解析
答案:B
每条省 \(16 - 8 = 8\) 字节:\(10^4 \times 8 = 8\times10^4\) 字节 \(\approx 80\) KB。
历年真题¶
2023 年 · 第 3 题¶
题目
阅读下述代码,请问修改 data 的 value 成员以存储 3.14,正确的方式是( )。
- A.
data.value = 3.14; - B.
value.data = 3.14; - C.
data->value = 3.14; - D.
value->data = 3.14;
答案与解析
答案:A
data 是 union 变量,成员访问用 .;-> 只用于指针(union Data* p = &data; p->value = 3.14;)。B/D 成员名与变量名写反。
易错小结¶
- union 大小 = 最大成员(对齐补齐),不是成员之和——成员之和是 struct 的算法;
- 同一时刻只有一个成员有效:写 A 读 B 得到的是位串重解释(写 1.0f 读 int 出 1065353216);
- 变量用
.、指针用->,union 与 struct 规则相同; - 花括号初始化只初始化第一个成员;
- 需要“同时有效”的多个值必须用 struct;union 只适合互斥解读、省空间、拆字节、判大小端。