嵌入式 C 语言联合体详解:高效内存共享与消息传递
理解嵌入式 C 语言中的联合体
在嵌入式系统中,联合体是一种强大的数据结构,允许在同一内存位置存储不同数据类型。本文梳理联合体基础,展示实战案例,解释为何它是内存受限和带宽敏感应用的首选工具。
嵌入式 C 中结构体与联合体的区别
前文介绍过结构体:将异构变量组合成单一实体。C 语言联合体也是组合不同类型的构造,但关键区别在于——所有成员共享同一内存地址。
入门示例
声明联合体与结构体几乎相同,只需将 struct 替换为 union:
union test { uint8_t c; uint32_t i; };
模板定义两个成员:c 占 1 字节,i 占 4 字节。创建该类型变量:
union test u1;
通过点运算符访问成员:
u1.i = 10; m = u1.c; // m 必须是 uint8_t
因联合体存储大小等于最大成员大小,u1 占 4 字节。写入 i 会覆盖 c 使用的同一内存,故两成员互斥。
是否需要共享内存空间?
设想两个嵌入式设备 设备 A 和 设备 B 交换状态、速度和位置数据(见图 1)。数据项及大小:

图 1
| 变量 | 大小(字节) | 含义 |
|---|---|---|
| power | 1 | 电池电量 |
| op_mode | 1 | 工作模式 |
| temp | 1 | 环境温度 |
| x_pos | 2 | X 坐标 |
| y_pos | 2 | Y 坐标 |
| vel | 2 | 速度 |
若 设备 B 需所有信息,结构体需 9 字节,每轮通信传输 9 字节帧(见图 2)。但许多实际场景中,三种消息类型互斥:有时仅状态、有时仅位置、有时仅速度。此时最大载荷为 4 字节(位置消息)。共享 4 字节缓冲区的联合体可容纳任一消息,将帧大小从 9 字节降至 4 字节(见图 3)。

图 2

图 3
联合体用于消息包
创建状态和位置两结构体,嵌入联合体与速度字段:
struct status_s { uint8_t power; uint8_t op_mode; uint8_t temp; }; struct position_s { uint16_t x_pos; uint16_t y_pos; }; union msg_union { struct status_s status; struct position_s position; uint16_t vel; };
联合体大小为 4 字节(position_s 大小),三种消息类型共享。
如何跟踪联合体活跃成员
接收端读取联合体时,必须知晓哪个成员有效。常用模式是标签联合体:额外字段记录活跃成员。在联合体前添加单字节 msg_type:
struct message_s { uint8_t msg_type; // 's'=状态, 'p'=位置, 'v'=速度 union { struct status_s status; struct position_s position; uint16_t vel; } msg; };
随联合体传输 msg_type 字节,接收端可正确解析载荷,无需预知其结构。
替代方案:动态内存分配
另一种方法是按消息类型动态分配内存。发送端设置 msg_type 并分配刚好足够的字节。接收端按 msg_type 解析数据,完成后释放内存。虽略微更省内存,但动态分配引入运行时开销和显式释放需求,故许多嵌入式开发者偏好静态联合体策略。
下期预告:联合体应用
联合体常用于从大字中提取小数据片段——解析通信协议或硬件寄存器时极具价值。敬请期待系列下文深入探讨这些用例。
浏览更多嵌入式 C 文章,访问 文章索引。
嵌入式