0Pricing
C++ Academy · 课时

联合体及其风险

在不同类型之间共享内存

联合体及其风险 是 CoddyKit 上的免费 C++ Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C++ Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C++ Academy 课程共包含 4 节课。

什么是联合体

union 允许多个成员共享同一块内存。同一时间只有一个成员有效,并且联合体的大小只取决于其最大成员。

#include <iostream>
union Number {
    int i;
    float f;
};
int main() {
    Number n;
    n.i = 42;
    std::cout << n.i << "\n";
}

联合体的大小

由于成员彼此重叠,联合体的 sizeof 等于其最大成员的大小(加上可能存在的对齐填充),而不是所有成员大小之和。

#include <iostream>
union Mix { char c; int i; double d; };
int main() {
    std::cout << sizeof(Mix) << "\n"; // size of double (often 8)
}

写入一个成员,读取另一个成员

写入一个成员后读取不同的成员是核心风险。这些位会被重新解释,通常会导致未定义行为或产生出人意料的结果。

#include <iostream>
union Bits { int i; float f; };
int main() {
    Bits b;
    b.f = 1.0f;
    std::cout << b.i << "\n"; // reads float bits as int
}

仅有一个活动成员

您最后写入的成员是唯一可以安全读取的成员。请自行跟踪哪个成员是“活动”成员;联合体不会记住这一点。

#include <iostream>
union Value { int i; double d; };
int main() {
    Value v;
    v.d = 3.14;        // d is active
    std::cout << v.d << "\n";
}

带标签的联合体模式

要安全地使用联合体,请将它与一个标签(枚举)配对,用于记录活动成员。这是 std::variant 的手动前身。

#include <iostream>
struct Tagged {
    enum { Int, Double } tag;
    union { int i; double d; };
};
int main() {
    Tagged t; t.tag = Tagged::Int; t.i = 7;
    if (t.tag == Tagged::Int) std::cout << t.i << "\n";
}

读取正确的成员

读取前请始终根据标签进行分支判断。读取错误的成员会产生垃圾值。

#include <iostream>
struct Tagged {
    enum { Int, Double } tag;
    union { int i; double d; };
};
void print(const Tagged& t) {
    if (t.tag == Tagged::Int) std::cout << "int " << t.i << "\n";
    else std::cout << "double " << t.d << "\n";
}
int main() { Tagged t{Tagged::Double}; t.d = 2.5; print(t); }

非平凡成员

将带有构造函数或析构函数的成员(例如 std::string)放入联合体,需要手动进行构造和析构。这很容易出错,也是应优先使用 std::variant 的另一个原因。

匿名联合体

匿名联合体没有名称;它的成员可以直接访问。它在结构体中很实用,但同样适用活动成员规则。

#include <iostream>
struct Packet {
    int kind;
    union { int code; char letter; };
};
int main() {
    Packet p; p.kind = 1; p.code = 99;
    std::cout << p.code << "\n";
}

联合体为何存在风险

风险总结:

  • 不会记住哪个成员处于活动状态。
  • 读取非活动成员属于未定义行为。
  • 非平凡类型需要手动管理生命周期。

联合体仍然有用的场景

联合体在底层工作中仍然很有用:重新解释字节、在内存紧张的嵌入式代码中节省内存,或匹配固定的二进制布局。对于日常的“几种类型中的一种”数据,请优先选择更安全的替代方案。

迈向 std::variant

std::variant 是一种类型安全的联合体,会替您跟踪活动类型,并在您访问错误类型时抛出异常。本课程的其余部分将对它进行探索。

快速检查

请检查您对联合体的理解。

回顾

您学习了联合体:

  • 所有成员共享同一块内存。
  • 只有最后写入的成员可以有效读取。
  • 使用标签(枚举)跟踪活动成员。
  • 在现代 C++ 中,为了安全性请优先使用 std::variant。

常见问题解答

「联合体及其风险」课时是免费的吗?

是的 — 「联合体及其风险」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C++ Academy 课程的其余内容,请升级到 CoddyKit PRO。 C++ Academy 课程共包含 4 节课。

「联合体及其风险」这节课中我会学到什么?

在不同类型之间共享内存 你通过在浏览器中直接运行的动手代码来练习 C++ Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C++ Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C++ Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「联合体及其风险」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C++ Academy 课中编写并运行代码吗?

能。每节 C++ Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 带作用域的 enum class
  2. 联合体及其风险
  3. std::variant
  4. std::visit
← 返回 C++ Academy