当前位置: 首页 > article >正文

C语言:共用体

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

1. 共用体的基本概念

  • 内存共享

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

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

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

2. 共用体的定义和使用

  • 定义共用体
union UnionName {
    data_type member1;
    data_type member2;
    ...
};
  • 声明和使用共用体变量
union UnionName varName;  // 声明共用体变量
  • 示例:定义一个通用数据容器
#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;
}
  • 运行示例
整数: 10
浮点数: 220.50
字符串: Hello
整数(被覆盖): 1819043144  // 结果可能因平台不同而异
  • 说明

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

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

  • 大小计算规则

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

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

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

4. 共用体的用途

  • 内存优化

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

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

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

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

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

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

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

6. 共用体的注意事项

  • 数据覆盖问题

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

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

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

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

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

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

#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;
}
  • 运行:
ID: 67305985
Flag: 1541
Checksum: 2055

8. 总结

  • 特点

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

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

    • 协议解析、内存优化、多态数据处理。

http://www.kler.cn/a/406814.html

相关文章:

  • 青少年编程等级考试C++一级,硬币反转问题
  • Linux线程_线程控制_线程库
  • 服务器产品
  • Java 对象头、Mark Word、monitor与synchronized关联关系以及synchronized锁优化
  • 136.flask内置jinja2模版使用
  • 「甲子光年」对话黄翔:从电子签回望中国SaaS“黄金十年”
  • Discuz论坛网站管理员的默认用户名admin怎么修改啊?
  • 【C++】友元friend的含义和用法
  • Java项目实战II基于SpringBoot的共享单车管理系统开发文档+数据库+源码)
  • pve 磁盘选错位置修改
  • MySQL系列之远程管理(安全)
  • 鸿蒙进阶-状态管理
  • 力扣-位运算-1【算法学习day.41】
  • 《深入浅出HTTPS​​​​​​​​​》读书笔记(9):对称加密算法
  • 第三十二篇 MobileNetV3论文翻译:《搜索 MobileNetV3》
  • React核心功能详解(一)
  • Debezium系列之:Debezium3版本源码阅读理解
  • Webpack之后,Rollup如何引领前端打包新潮流?(1)
  • SQLite Having 子句
  • C语言程序编译和链接
  • 利用 GitHub 和 Hexo 搭建个人博客【保姆教程】
  • seq2seq attention详解
  • 用nextjs开发时遇到的问题
  • 安卓应用安装过程学习
  • 苹果Siri将搭载大型语言模型,近屿智能抢占AIGC大模型人才培养高地
  • 掌握Go语言中的异常控制:panic、recover和defer的深度解析