定义共用体只需要把 struct 换成 union,其他语法一模一样:union 共用体名 { 成员列表 };,末尾分号同样不能漏。声明变量、访问成员也全照旧:Data d; d.i。所以这一节的"新知识"其实只有一条:改一个关键字,语义就从"并排"变成"重叠"。
8.1.1(结构体定义)和 7.x 的语法全都直接照搬。唯一要小心的是"不能想当然"——union 的定义看起来和 struct 一样,但行为完全不同。这一节把语法钉牢,下一节 8.5.2 用 sizeof 和实例05 把"重叠"这个事实实测出来。
一、定义:union 换 struct
union Data { // 只把 struct 换成 union
int i;
float f;
char c;
}; // 末尾分号照旧不能漏(8.1.1 的第一大坑)
读成大白话:"凡是叫 Data 的东西,可以在同一块内存上,被当成 int 或 float 或 char 来用。"
和结构体定义并排看:
struct Data { int i; float f; char c; }; // 三间房:4 + 4 + 1 → 12 字节(含对齐)
union Data { int i; float f; char c; }; // 一间房:取最大 4 → 4 字节
语法上唯一的差别就是那个关键字——一个词的差别,12 字节变 4 字节。
二、声明变量:和结构体完全一样
union Data d1; // 一个共用体变量
union Data d2, d3; // 一行多个
Data d4; // 如果在 C++ 里(或用了 typedef),可以省 union
三种写法(和 8.1.1 一一对应):
// ① 先定义类型,用到再声明变量(推荐)
union Data { int i; float f; char c; };
Data d;
// ② 定义类型时顺便声明变量
union Data { int i; float f; char c; } d1, d2;
// ③ 匿名共用体(只用一次)
union { int i; float f; char c; } d;
加 typedef 也可以(8.2 的老套路):
typedef union Data {
int i;
float f;
char c;
} Data; // 之后可以直接写 Data d;
三、访问成员:还是那个点
Data d;
d.i = 10; // 给 i 这一"解读方式"赋值
cout << d.i << endl; // 10
d.f = 3.14f; // ⚠️ 这次赋值会覆盖刚才的 i(下一节讲)
cout << d.f << endl; // 3.14
语法和结构体一模一样:变量.成员。
但语义完全不同——这里每一个赋值都在"用同一块内存":
| 结构体 | 共用体 | |
|---|---|---|
d.i = 10; 之后再 d.f = 3.14; |
两个人各占一间,互不影响 | 后写的把先写的覆盖掉 |
能不能同时读 d.i 和 d.f |
能,都是有效值 | 不能,只有一个有效 |
一句话记牢:共用体里,"写一个成员"就是"把另外几个成员废掉"。
四、初始化:只能给第一个成员初始化
这是一个容易踩的坑:
union Data d = {10}; // ✅ 只初始化第一个成员(i = 10)
union Data e = {1, 2, 3}; // ❌ 编译错:共用体只能初始化一个成员
为什么?因为它们共用同一块内存——你没法同时把 10、2.5、'A' 三种值塞进同一间房。标准规定:初始化列表里只能有一个值,且对应第一个成员。
想要别的成员有初值,只能声明之后再赋值:
union Data d;
d.f = 3.14f; // 声明后赋值,不受限制(但会覆盖其他解读)
五、动手 Demo:定义、赋值、看地址
#include <iostream>
using namespace std;
union Data {
int i;
float f;
char c;
};
int main() {
// ① 只初始化第一个成员
union Data d1 = {10};
cout << "d1.i = " << d1.i << endl;
// ② 声明后逐个赋值(相互覆盖)
union Data d2;
d2.i = 100;
cout << "写 i 后 d2.i = " << d2.i << endl;
d2.f = 3.14f; // 覆盖
cout << "写 f 后 d2.f = " << d2.f << endl;
cout << "此时再读 d2.i = " << d2.i << "(不是 100 了)" << endl;
d2.c = 'A'; // 又覆盖
cout << "写 c 后 d2.c = " << d2.c << endl;
cout << "此时再读 d2.f = " << d2.f << "(也被破坏了)" << endl;
// ③ 地址证明:"两个成员住同一个门牌号"
cout << "&d2 = " << &d2 << endl;
cout << "&d2.i = " << &d2.i << "(相同)" << endl;
cout << "&d2.f = " << &d2.f << "(相同)" << endl;
cout << "&d2.c = " << &d2.c << "(相同)" << endl;
// ④ 和结构体对比
struct S { int i; float f; char c; };
cout << "sizeof(union Data) = " << sizeof(Data) << endl;
cout << "sizeof(struct S) = " << sizeof(S) << endl;
return 0;
}
看到什么算成功:
d1.i = 10
写 i 后 d2.i = 100
写 f 后 d2.f = 3.14
此时再读 d2.i = (一个奇怪的值,不是 100)
写 c 后 d2.c = A
此时再读 d2.f = (也被破坏了)
&d2 = 0x...
&d2.i = 0x...(相同)
&d2.f = 0x...(相同)
&d2.c = 0x...(相同)
sizeof(union Data) = 4
sizeof(struct S) = 12
sizeof(struct S)在你机器上也可能是 8 或 12(看对齐),重点是它比union的 4 大得多。
两个"后写覆盖先写"的证据:
- 写 d2.f 之后,d2.i 不再是 100;
- 写 d2.c 之后,d2.f 不再是 3.14。
一个"同居"的证据:四个地址打印出来完全一样。
30 秒实验:把 d2.c = 'A'; 这一句挪到最前面(在 d2.i = 100; 之前),重新运行——这次最后读 d2.i 就是 100(因为 i 是最后写的)。谁最后写,谁说了算。
六、常见误区
- "共用体可以像结构体那样多个成员同时初始化"——不行。只能给第一个成员一个初值(
union Data d = {10};),其他成员只能声明后赋值。 - "定义时忘了末尾分号没关系"——和结构体一样是硬伤(8.1.1)。漏了会让后面一整段代码报莫名其妙的错。
- "共用体成员可以同时使用"——绝对不行。"最后一次写的是哪个成员,就只有哪个成员有效",其他成员读出来是"被搅乱的字节"。
- "
union和struct只是关键字不同,功能一样"——语法像,语义相反(并排 vs 重叠)。这是本章最需要分清的对比。
自测三问(点开对答案)
1. 定义共用体的语句里,`struct` 该换成什么?其他有变化吗?——换成 `union`。语法(花括号、末尾分号、成员列表)完全一样。 2. `union Data d = {10};` 合法吗?`{1, 2, 3}` 呢?——第一个合法(初始化第一个成员);第二个**不合法**,共用体只能初始化一个成员。 3. 怎么用代码证明两个成员"住在同一块内存"?——打印 `&d.i`、`&d.f`、`&d.c`,**三者相同**。- ☐ 8.5.2 共用体的大小(下一节)
相关笔记
- 共用体 —— 上一节:共用体是什么
- 共用体的大小 —— 下一节:取最大成员(含实例05)
- 结构体定义 —— 逐句对照的语法模板
- 重命名数据类型 —— 给共用体起别名
- 🔷 C++基础 · 索引 —— 全课路线图