C语言:共用体

在C语言中,共用体(union)是一种特殊的用户自定义数据类型,它和结构体类似,但有一个关键区别:共用体中所有成员共用一段内存空间。这使得共用体特别适用于需要节省内存的场景。

1. 共用体的基本概念

  • 内存共享

    • 共用体中的所有成员共享一块内存。
    • 共用体的大小由其最大成员的大小决定,而不是所有成员大小的总和。
  • 单一有效成员

    • 在同一时间内,共用体中只能有一个成员是有效的,访问其他成员可能会导致未定义行为。
  • 适用场景

    • 数据类型互斥使用的场景。
    • 需要节省内存的嵌入式开发或低资源场景。

2. 共用体的定义和使用

  • 定义共用体
c 复制代码
union UnionName {
    data_type member1;
    data_type member2;
    ...
};
  • 声明和使用共用体变量
c 复制代码
union UnionName varName;  // 声明共用体变量
  • 示例:定义一个通用数据容器
c 复制代码
#include <stdio.h>
#include <windows.h>

union Data {
    int i;
    float f;
    char str[20];
};

int main() {
    union Data data;
	SetConsoleOutputCP(65001);

    // 使用整数成员
    data.i = 10;
    printf("整数: %d\n", data.i);

    // 使用浮点数成员
    data.f = 220.5;
    printf("浮点数: %.2f\n", data.f);

    // 使用字符串成员
    // 注意:会覆盖之前的值
    snprintf(data.str, sizeof(data.str), "Hello");
    printf("字符串: %s\n", data.str);

    // 再次访问其他成员会导致未定义行为
    printf("整数(被覆盖): %d\n", data.i);

    return 0;
}
  • 运行示例
c 复制代码
整数: 10
浮点数: 220.50
字符串: Hello
整数(被覆盖): 1819043144  // 结果可能因平台不同而异
  • 说明

由于共用体成员共享同一块内存,赋值 data.f 后会覆盖 data.i 的值,类似地,赋值 data.str 也会覆盖其他成员。

3. 共用体的大小与内存布局

  • 大小计算规则

共用体的大小等于其最大成员的大小,并且对齐到成员中最大类型的对齐要求。

  • 示例:
c 复制代码
union Example {
    char c;      // 1 字节
    int i;       // 4 字节
    double d;    // 8 字节
};
printf("共用体大小: %lu 字节\n", sizeof(union Example));
  • 输出
c 复制代码
共用体大小: 8 字节
  • 原因

    • double 是最大成员(8字节),因此共用体大小为8字节。

4. 共用体的用途

  • 内存优化

    • 在嵌入式系统中,共用体可以减少内存使用,因为多个变量共用一块内存。
  • 数据类型转换

    • 共用体可以用于实现不同类型之间的低级别数据转换,如将整数解释为浮点数或字节数组。
  • 示例:强制类型转换。

c 复制代码
union {
    float f;
    int i;
} u;
u.f = 3.14f;
printf("存储为整数: %d\n", u.i);
  • 协议解析

    • 用于网络数据包或文件格式解析,表示一个数据段可能有多种解释方式。
  • 多态数据结构

    • 共用体常用于模拟一种简单的多态结构,可以存储不同类型的数据但只在一个时间点使用一个类型。

5. 共用体与结构体的区别

特性 共用体(union) 结构体(struct)
内存分配 所有成员共用一块内存 每个成员独立分配内存
大小 由最大成员的大小决定 等于所有成员大小之和
访问限制 同时只能使用一个成员 可以同时使用所有成员
适用场景 数据互斥使用、节省内存的场景 含有多个属性的复杂数据结构

6. 共用体的注意事项

  • 数据覆盖问题

    • 修改一个成员会覆盖其他成员的值,因此要小心避免错误使用。
c 复制代码
union Test {
    int i;
    float f;
};

union Test t;
t.i = 42;
t.f = 3.14; // 此时 t.i 的值被覆盖
  • 只在合适场景下使用

    • 共用体适合处理数据互斥使用的场景,如果需要同时操作多个成员,建议使用结构体。
  • 对齐与字节序

    • 共用体的内存对齐可能会影响跨平台兼容性。
    • 例如,某些平台对 double 类型的对齐要求可能比 int 更严格。

7. 扩展案例:模拟网络数据包

以下示例展示共用体在协议解析中的应用。

c 复制代码
#include <stdio.h>
#include <windows.h>

union Packet {
    char rawData[8];
    struct {
        int id;
        short flag;
        short checksum;
    } parsed;
};

int main() {
    union Packet packet;
	SetConsoleOutputCP(65001);

    // 设置原始数据
    packet.rawData[0] = 0x01;
    packet.rawData[1] = 0x02;
    packet.rawData[2] = 0x03;
    packet.rawData[3] = 0x04;
    packet.rawData[4] = 0x05;
    packet.rawData[5] = 0x06;
    packet.rawData[6] = 0x07;
    packet.rawData[7] = 0x08;
    // 通过结构解析数据
    printf("ID: %d\n", packet.parsed.id);
    printf("Flag: %d\n", packet.parsed.flag);
    printf("Checksum: %d\n", packet.parsed.checksum);

    return 0;
}
  • 运行:
c 复制代码
ID: 67305985
Flag: 1541
Checksum: 2055

8. 总结

  • 特点

    • 共用体通过共享内存节省空间,适合嵌入式和资源有限的应用。
    • 提供了灵活性,允许以多种方式解释同一段数据。
  • 注意事项

    • 共用体操作需要注意数据覆盖问题,只有在明确知道数据互斥使用的情况下才推荐使用。
  • 常见应用场景

    • 协议解析、内存优化、多态数据处理。
相关推荐
Scott9999HH2 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社3 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海3 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖4 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大7 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai7 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜7 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa7 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio