66.嵌入式C语言进阶:共用体(Union)实战指南——单片机内存省一半的神级技巧

一、共用体是什么?和结构体有什么区别?

很多人会把共用体和结构体混淆,其实它们的核心差异在于内存分配方式:

  • 结构体(struct):每个成员单独分配独立内存,总大小是所有成员大小之和。
  • 共用体(union):所有成员共享同一块内存空间,同一时间只能有效保存其中一个成员的数据。

举个形象的例子:结构体就像一间被隔成多个小房间的公寓,每个房间住不同的人;共用体则是一间多功能房,白天当办公室,晚上当卧室,同一时间只能做一件事。

二、共用体的3个核心特点

  1. 内存大小等于最大成员的大小:比如一个共用体里有int(4字节)和char buf4(4字节),那这个共用体的大小就是4字节,刚好能容纳最大的成员。
  2. 成员互斥,修改一个覆盖其他:给共用体的一个成员赋值后,其他成员的值会被覆盖,因为它们共享同一块内存。
  3. 适合数据类型转换与拆分:可以把一个整数拆成字节数组,也可以把字节数组拼成整数,在串口协议解析、传感器数据处理中特别好用。

三、基础代码示例:理解内存共享

我们先看一个最基础的共用体示例,直观感受它的内存共享特性:

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

// 定义共用体:int和char buf[4]共享4字节内存
union Data
{
    unsigned int num;    // 4字节
    unsigned char buf[4]; // 4字节
};

int main(void)
{
    union Data val;
    val.num = 0x12345678; // 给int成员赋值
    printf("第0字节: 0x%02X\n", val.buf[0]);
    printf("第1字节: 0x%02X\n", val.buf[1]);
    return 0;
}

运行结果

text 复制代码
第0字节: 0x78
第1字节: 0x56

这说明在小端模式的单片机中,整数的低字节存在低地址,高字节存在高地址,共用体直接帮我们完成了整数到字节的拆分。

四、嵌入式实战场景:串口协议解析

在单片机开发中,串口通信经常需要把整数拆成字节发送,或者把接收到的字节拼成整数,用共用体可以避免手动位运算,代码更简洁高效。

场景1:整数拆分成字节发送

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

// 定义共用体:int和字节数组共享内存
typedef union {
    unsigned int data;
    unsigned char bytes[4];
} IntToBytes;

int main()
{
    IntToBytes converter;
    converter.data = 0xAABBCCDD;
    
    // 直接通过bytes成员获取拆分后的字节
    printf("拆分后的字节:\n");
    for(int i=0; i<4; i++){
        printf("字节%d: 0x%02X\n", i, converter.bytes[i]);
    }
    
    // 模拟串口发送:依次发送converter.bytes[0]到converter.bytes[3]
    return 0;
}

场景2:字节拼成整数接收

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

typedef union {
    unsigned int data;
    unsigned char bytes[4];
} BytesToInt;

int main()
{
    BytesToInt converter;
    // 模拟接收到的字节流
    unsigned char recv_buf[4] = {0xDD, 0xCC, 0xBB, 0xAA};
    
    // 把接收到的字节复制到共用体的bytes成员
    memcpy(converter.bytes, recv_buf, 4);
    // 直接通过data成员获取拼接后的整数
    printf("拼接后的整数:0x%08X\n", converter.data);
    
    return 0;
}

五、进阶技巧:共用体+结构体嵌套操作寄存器

在单片机中,我们经常需要操作硬件寄存器,既要能整体读写寄存器,又要能单独修改某一个位,这时候用共用体+结构体嵌套的方式会非常方便:

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

// 定义结构体:拆分寄存器的各个位
typedef struct {
    unsigned char bit0 : 1;  // 位0
    unsigned char bit1 : 1;  // 位1
    unsigned char bit2 : 1;  // 位2
    unsigned char bit3 : 1;  // 位3
    unsigned char reserved : 4; // 保留位
} RegBits;

// 定义共用体:整体寄存器和位结构体共享内存
typedef union {
    unsigned char reg_val; // 整体寄存器值
    RegBits bits;          // 拆分后的位
} Register;

int main()
{
    Register reg;
    reg.reg_val = 0x03; // 整体赋值
    
    // 单独修改位2,不影响其他位
    reg.bits.bit2 = 1;
    printf("修改后的寄存器值:0x%02X\n", reg.reg_val);
    
    return 0;
}

六、使用共用体的注意事项

  1. 不要同时读取多个成员:因为成员共享内存,同时读取会得到错误的值,要确保当前只有一个成员是有效的。
  2. 注意字节序问题:不同单片机可能是大端或小端模式,跨设备通信时要统一字节序。
  3. 内存对齐:共用体的大小会对齐到最大成员的对齐数的整数倍,比如如果有double成员,大小会对齐到8字节。

七、总结

共用体是嵌入式C语言中非常实用的技巧,它能帮你在内存有限的单片机中节省RAM,同时简化数据类型转换和协议解析的代码。在串口通信、传感器数据处理、寄存器操作等场景中,合理使用共用体能让你的代码更简洁、更高效。

如果你觉得这篇文章对你有帮助,欢迎点赞收藏,也可以在评论区分享你使用共用体的实战经验!

相关推荐
liulilittle11 分钟前
无锁并发容器的设计与实现原理
开发语言·c++·set·map·并发·无锁·lock-free
朋克洛德的码农35 分钟前
Go并发-sync包四剑客:Mutex、RWMutex、WaitGroup、Once-从入门到原理
开发语言·后端·golang
zcmodeltech1 小时前
智能制造教学实训沙盘模型多系统协同控制系统设计:基于STM32与Modbus RTU的工业机器人-智慧工厂-数字孪生全场景联动方案
数据库·stm32·嵌入式硬件·机器人·制造·多分类
程序员与背包客_CoderZ1 小时前
Linux D-Bus通信协议详解:从入门到C/C++编码实战
linux·服务器·c语言·c++·嵌入式硬件·嵌入式软件·dbus
淼澄研学2 小时前
Kimi API黑产倒卖技术解析与Python合规接入指南
开发语言·网络·python
冻柠檬飞冰走茶2 小时前
PTA基础编程题目集 7-35有理数均值(C++语言实现)
开发语言·数据结构·c++·算法·均值算法
wangchen_02 小时前
C++正则表达式
开发语言·c++·正则表达式
何以解忧,唯有..2 小时前
Python 元组(tuple)详解:使用、遍历与排序
开发语言·python
KANGBboy2 小时前
Python eval安全隐患
开发语言·python
IT爱学堂2 小时前
java版数据结构和算法+AI算法和技能学习指南
java·开发语言