C/C++联合体(union)完全指南:从内存共享到高级用法

1. 联合体基础概念

联合体(union)是一种特殊的数据类型,允许在相同内存位置存储不同的数据类型 ,但同一时间只能使用一个成员

cpp 复制代码
union Data {
    int i;
    float f;
    char str[20];
};

核心特性

  • 所有成员共享同一块内存

  • 大小由最大成员决定

  • 同一时间只有一个成员有效

  • 常用于节省内存类型转换场景

2. C语言中的联合体

2.1 基本用法

cpp 复制代码
union Number {
    int integer;
    float real;
};

union Number num;
num.integer = 10;    // 此时存储的是整数
printf("%d", num.integer);

num.real = 3.14;     // 现在存储的是浮点数,整数值被覆盖
printf("%f", num.real);

2.2 内存布局示例

cpp 复制代码
union {
    int a;    // 占用4字节
    char b;   // 占用1字节(与a共享前1字节)
} u;

3. C++中的联合体增强

3.1 成员函数(C++11起)

cpp 复制代码
union SmartUnion {
    int x;
    double y;
    
    void printX() { cout << x; }  // C++允许成员函数
};

3.2 带构造函数/析构函数的类成员(C++11)

cpp 复制代码
union ComplexUnion {
    std::string str;  // 可以包含非POD类型(需要手动管理)
    int num;
    
    ComplexUnion() {}  // 需要自定义构造/析构
    ~ComplexUnion() {}
};

4. 经典应用场景

4.1 类型转换

cpp 复制代码
union Converter {
    float f;
    unsigned int u;
} conv;

conv.f = 3.14;
printf("IEEE754表示: %X", conv.u);  // 查看浮点数的二进制表示

4.2 协议解析

cpp 复制代码
union Packet {
    struct {
        uint8_t header;
        uint16_t payload;
    } fields;
    uint8_t raw[3];  // 以字节数组形式访问
};

4.3 内存优化

cpp 复制代码
union Variant {
    int i;
    char c;
    // 共用4字节内存而不是4+1
};

5. 联合体vs结构体

特性 联合体(union) 结构体(struct)
内存使用 共享内存 每个成员独立内存
存储能力 只能存一个成员值 可存储所有成员值
大小计算 由最大成员决定 所有成员大小之和+对齐
访问控制 所有成员总是public 可设置访问权限

6. 高级技巧与注意事项

6.1 匿名联合体(C11/C++)

cpp 复制代码
struct Device {
    enum { INT, FLOAT } type;
    union {  // 匿名联合体
        int i;
        float f;
    };
};

Device d;
d.type = Device::FLOAT;
d.f = 2.718;  // 直接访问

6.2 类型双关(Type Punning)

cpp 复制代码
union {
    uint32_t bits;
    float value;
} converter;

converter.bits = 0x40490FDB;  // 直接操作二进制位
printf("%f", converter.value); // 输出3.141592

⚠️ 注意:C++中类型双关可能违反严格别名规则,推荐使用memcpy代替

6.3 联合体位域

cpp 复制代码
union StatusRegister {
    uint16_t raw;
    struct {
        uint16_t error : 1;   // 第0位
        uint16_t ready : 1;   // 第1位
        uint16_t : 14;        // 保留位
    } bits;
};

7. 实际工程中的坑

  1. 初始化问题

    cpp 复制代码
    union U { int a; float b; };
    union U u = { .a = 10 };  // C99指定初始化
  2. C++非POD类型管理

    cpp 复制代码
    union {
        std::string s;  // 必须手动管理生命周期
        int x;
    } u;
    
    new (&u.s) std::string("hello");  // 手动构造
    u.s.~string();                    // 手动析构
  3. 字节序问题

    cpp 复制代码
    union EndianTest {
        uint32_t num;
        uint8_t bytes[4];
    } test = {0xAABBCCDD};
    // 输出取决于平台字节序

8. C++17增强:带标签联合体

cpp 复制代码
#include <variant>  // C++17引入

std::variant<int, float, std::string> v;
v = 3.14f;  // 当前存储float
if (auto p = std::get_if<float>(&v)) {
    cout << *p;  // 安全访问
}

9. 总结

联合体是内存高效需要谨慎使用的工具:

  • 适合:底层编程、协议解析、类型转换

  • 避免:需要同时存储多个值的场景

  • ⚠️ 注意:类型安全、生命周期管理问题