每日一个C语言知识:C 共用体

什么是共用体?

共用体是一种特殊的数据类型,它允许您在不同的时间(不是同时)存储不同类型的数据。它的所有成员共享同一块内存空间。

核心思想: 共用体中所有成员的起始地址都是相同的。这意味着,修改一个成员的值,会直接影响其他成员的值。


如何定义共用体?

使用 union 关键字来定义,语法与结构体 struct 非常相似。

c 复制代码
union 共用体名称 {
    数据类型 成员1;
    数据类型 成员2;
    // ... 更多成员
};

示例:定义一个共用体

c 复制代码
union Data {
    int i;      // 4字节(通常)
    float f;    // 4字节(通常)
    char str[20]; // 20字节
};

在这个例子中:

  • union Data 类型的一个变量将占用 20个字节 的内存空间(因为它的最大成员 str[20] 是20字节)。
  • 这20个字节被三个成员 i, f, str 共享

如何使用共用体?

与结构体一样,使用成员运算符 . 来访问成员。

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

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

int main() {
    union Data data; // 声明一个共用体变量

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

    // 使用浮点数成员
    data.f = 220.5;
    printf("data.f : %.2f\n", data.f); // 输出:data.f : 220.50
    // 注意:此时 data.i 的值已经被破坏,因为它和 data.f 共享内存

    // 使用字符数组成员
    strcpy(data.str, "C Programming");
    printf("data.str : %s\n", data.str); // 输出:data.str : C Programming
    // 注意:此时 data.i 和 data.f 的值都已被破坏

    return 0;
}

关键观察:

当你给 data.f 赋值后,之前存储在 data.i 中的值就变得无意义了。同样,当你给 data.str 赋值后,data.f 的值也被破坏了。因为它们都使用同一块内存。


共用体 vs. 结构体

为了加深理解,我们来看一个与结构体的直观对比。

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

// 结构体
struct MyStruct {
    int i;
    float f;
    char str[20];
};

// 共用体
union MyUnion {
    int i;
    float f;
    char str[20];
};

int main() {
    struct MyStruct s;
    union MyUnion u;

    printf("结构体大小 : %lu 字节\n", sizeof(s));
    printf("共用体大小 : %lu 字节\n", sizeof(u));

    return 0;
}

输出可能是:

复制代码
结构体大小 : 28 字节
共用体大小 : 20 字节

为什么?

  • 结构体: 每个成员都有自己独立的内存空间。s.i, s.f, s.str 的地址都不同。总大小是所有成员大小之和(可能加上一些对齐填充)。
  • 共用体: 所有成员共享同一块内存。总大小由其最大成员 的大小决定(这里是20字节的str)。

共用体的主要用途

  1. 节省内存:当你知道一个变量在程序的不同阶段会用于存储不同类型的数据,并且这些数据不会同时需要时,使用共用体可以大幅节省内存。这在嵌入式系统等内存受限的环境中尤其重要。

  2. 解释同一数据的多种方式 :例如,你可以用一个共用体来将一个4字节的 int 数据,按照 char 数组的方式逐个字节地解析和处理。

    c 复制代码
    union IntConverter {
        int number;
        unsigned char bytes[4];
    };
    
    union IntConverter converter;
    converter.number = 0x12345678;
    
    // 现在你可以通过 bytes 数组访问整数的每一个字节
    for(int i = 0; i < 4; i++) {
        printf("Byte %d: 0x%02X\n", i, converter.bytes[i]);
    }
  3. 实现变体记录:在通信协议或文件格式中,一个数据包可能根据"类型"字段的不同,后面跟着不同类型的数据。共用体可以完美地模拟这种情况。


注意事项(重要!)

  • 只能使用一个成员:在某一时刻,只有一个成员是有效的。你最后一次赋值的成员才是那个你可以正确读取的成员。

  • 未定义行为:读取一个你没有写入的成员,其结果是未定义的(可能得到垃圾值,也可能导致程序崩溃)。

  • 初始化 :只能初始化共用体的第一个成员

    c 复制代码
    union Data data = {10}; // 正确,初始化 i 为 10
    // union Data data = {3.14}; // 错误!不能这样初始化 f

总结

特性 结构体 共用体
关键字 struct union
内存 成员内存独立 成员内存共享
总大小 所有成员大小之和 + 对齐填充 最大成员的大小 + 对齐填充
用途 存储一组相关的、同时存在的数据 存储一组互斥的、不同时存在的数据

共用体是C语言中"用空间换时间"以及进行底层数据操作的精妙工具。理解并正确使用它,能让你的程序更加高效和灵活。

相关推荐
dqsh066 小时前
树莓派5+Ubuntu24.04 LTS CH348 / CH9344 驱动安装 保姆级教程
linux·c语言·单片机·嵌入式硬件·iot
奔跑吧邓邓子7 小时前
【C语言实战(80)】C语言实战:从复盘到进阶,解锁编程新高度
c语言·进阶·复盘·终篇
@曾记否7 小时前
如何在Keil5中在没有硬件支持的情况下使用Keil的模拟器(Simulator) + 调试窗口输出进行调试
c语言·stm32
烛衔溟8 小时前
C语言多级指针与函数指针:指针的高级用法
c语言·算法
树在风中摇曳11 小时前
C语言动态内存管理:从基础到进阶的完整解析
c语言·开发语言·算法
biter down12 小时前
C 语言17:位操作符 & | ^:从二进制编码到大小端
c语言·开发语言
永远都不秃头的程序员(互关)12 小时前
C 语言文件读写初探:打开数据之门 [特殊字符]
c语言
楼田莉子16 小时前
Linux学习:进程的控制
linux·运维·服务器·c语言·后端·学习
云雾J视界18 小时前
C语言位运算深度应用:嵌入式硬件寄存器控制与低功耗优化实践
c语言·stm32·嵌入式硬件·低功耗·数据压缩·寄存器
努力努力再努力wz19 小时前
【Linux进阶系列】:线程(下)
linux·运维·服务器·c语言·数据结构·c++·算法