• C++
  • Union(共用体)教程

  • @ 2025-8-29 14:26:33

Union(共用体)教程

在 C/C++ 中,union(共用体)是一种特殊的数据结构,其核心特点是所有成员共享同一块内存空间,而非像结构体(struct)那样为每个成员分配独立内存。这意味着共用体的大小由其占用内存最大的成员决定,且修改一个成员会覆盖其他成员的值。

一、基本定义与语法

共用体通过 union 关键字声明,语法与结构体类似,成员间用逗号分隔,末尾需加分号。

语法格式

union 共用体名 {
    数据类型1 成员名1;
    数据类型2 成员名2;
    // ... 其他成员
};

示例:定义并使用共用体

#include <stdio.h>

// 定义共用体,成员共享内存
union Data {
    int i;      // 4字节(通常)
    float f;    // 4字节(通常)
    char c;     // 1字节
};

int main() {
    union Data data;
    // 1. 查看共用体大小(由最大成员决定,此处为4字节)
    printf("共用体大小:%zu 字节\n", sizeof(data));  // 输出:4 字节

    // 2. 赋值并访问成员(修改一个成员会覆盖其他成员)
    data.i = 0x12345678;  // 给int成员赋值(十六进制)
    printf("data.i = %#x\n", data.i);       // 输出:0x12345678
    printf("data.c = %#x\n", data.c);       // 输出:0x78(仅取内存低1字节)

    data.f = 3.14f;       // 给float成员赋值,覆盖int成员的内存
    printf("data.f = %.2f\n", data.f);      // 输出:3.14
    printf("data.i = %#x\n", data.i);       // 输出:随机值(内存已被float覆盖)

    return 0;
}

二、核心特性:内存共享

  1. 内存共用:所有成员的首地址相同,共用同一块内存,修改一个成员会直接覆盖其他成员的内存数据(因为内存被复用)。
    例:上述代码中,data.i 赋值后,data.c 只能读取 data.i 内存的低1字节;赋值 data.f 后,data.i 的值因内存被覆盖而失效。

  2. 大小由最大成员决定:共用体的总内存大小 = 其最大成员的内存大小(需考虑内存对齐,与结构体对齐规则一致)。
    例:若共用体包含 char(1字节)和 double(8字节),则共用体大小为 8 字节。

  3. 同一时间只能用一个成员:因内存共享,同一时刻只能安全访问“最近赋值的成员”,访问未赋值的成员会得到无意义的垃圾值。

三、常见使用场景

共用体的核心价值是节省内存,适用于“同一内存区域在不同场景下存储不同类型数据”的场景:

1. 节省内存(内存受限场景)

当数据不会同时使用时,用共用体替代结构体,减少内存占用。
例:存储“学生信息”,学生要么填“学号(int)”,要么填“临时编号(char[10])”,二者不同时使用:

union ID {
    int student_id;    // 学号(4字节)
    char temp_id[10];  // 临时编号(10字节)
};

struct Student {
    char name[20];
    union ID id;       // 共用体节省内存,避免同时存储两种编号
};

2. 访问同一内存的不同类型

通过不同类型的成员,解析同一块内存的二进制数据(常用于底层数据处理,如硬件寄存器、数据格式转换)。
例:将 int 类型的4字节内存,按字节拆分读取:

union IntByte {
    int i;
    char bytes[4];  // 用char数组拆分int的4个字节
};

int main() {
    union IntByte ib;
    ib.i = 0x11223344;
    // 按字节输出int的4个部分(小端机器下,低字节存低地址)
    for (int j = 0; j < 4; j++) {
        printf("字节%d:%#x\n", j, ib.bytes[j]);  // 输出:0x44、0x33、0x22、0x11
    }
    return 0;
}

3. 与结构体结合实现“变体类型”

通过“结构体+共用体”,实现类似“多类型数据”的存储(如C++的variant简化版)。
例:存储“不同类型的消息”,消息要么是int,要么是char*

// 标记消息类型
enum MsgType { INT_MSG, STR_MSG };

struct Message {
    enum MsgType type;  // 标记当前共用体使用的成员
    union Content {     // 消息内容,二选一
        int num;
        char* str;
    } content;
};

// 使用时通过type判断成员类型,避免访问错误
void printMsg(struct Message msg) {
    if (msg.type == INT_MSG) {
        printf("整数消息:%d\n", msg.content.num);
    } else if (msg.type == STR_MSG) {
        printf("字符串消息:%s\n", msg.content.str);
    }
}

四、与结构体(struct)的区别

对比维度 union(共用体) struct(结构体)
内存分配 所有成员共享同一块内存 为每个成员分配独立内存,总大小=成员大小之和(含对齐)
大小计算 大小 = 最大成员的大小(含对齐) 大小 = 各成员大小之和(含对齐)
成员访问 同一时间只能安全访问一个成员 可同时访问所有成员
数据覆盖 修改一个成员会覆盖其他成员 修改一个成员不影响其他成员
核心用途 节省内存、解析同一内存的不同类型 存储多个相关且需同时存在的数据

五、注意事项

  1. 避免同时访问多个成员:因内存共享,未赋值的成员值是垃圾值,访问会导致逻辑错误。
  2. 成员的初始化:共用体只能初始化第一个成员(C语言),或通过指定成员初始化(C++11及以后)。
    例:union Data d = {10};(初始化第一个成员i);C++中可写union Data d = {.f = 3.14f};
  3. 不支持引用成员:C/C++中,共用体的成员不能是引用(引用必须绑定唯一对象,与内存共享冲突)。
  4. 内存对齐:共用体的对齐规则与结构体一致,需注意成员类型的对齐要求(如chardouble组成的共用体,会按double的8字节对齐)。
  5. C++中的扩展:C++中,共用体可包含有构造函数/析构函数的成员(需手动管理生命周期),但C语言不支持。

六、总结

  • union 的核心是内存共享,目的是节省内存,适用于“同一内存区域存储不同类型数据”的场景。
  • 使用时需通过“类型标记”(如枚举)明确当前访问的成员,避免数据覆盖导致的错误。
  • 与结构体的本质区别是“内存是否共享”,需根据数据是否“同时存在”选择使用。

0 条评论

目前还没有评论...