使用union来打包/拆包数据

联合的成员存储在共享存储区中。这是使我们能够找到有趣的工会应用程序的关键功能。
考虑下面的联合:

  1. union {
  2. uint16_t   word;
  3. struct {
  4. uint8_t   byte1;
  5. uint8_t   byte2;
  6. };
  7. } u1;

复制代码

此union内部有两个成员:第一个成员“ word”是一个两字节的变量。第二个成员是两个单字节变量的结构。为联合分配的两个字节在其两个成员之间共享。
分配的内存空间可以如下图1所示。


图1
“ word”变量是指整个分配的内存空间,“ byte1”和“ byte2”变量是指构成“ word”变量的一字节区域。我们如何使用此功能?假设您有两个单字节变量“ x”和“ y”,应将其组合以产生单个两字节变量。

在这种情况下,您可以使用上述联合并为结构成员分配“ x”和“ y”,如下所示:

  1. u1.byte1 = y;
  2. u1.byte2 = x;

复制代码

现在,我们可以读取并集的“ word”成员,以获得由“ x”和“ y”变量组成的两字节变量(参见图2)。


图2
上面的示例显示了使用并集将两个一个字节的变量打包为单个两个字节的变量。我们也可以做相反的事情:将两个字节的值写入“ word”,然后通过读取“ x”和“ y”变量将其解压缩为两个一个字节的变量。将值写入工会的一个成员并读取工会的另一个成员有时被称为“数据修剪”。

处理器字节序
当使用联合对数据进行打包时,我们需要注意处理器的字节顺序。正如罗伯特·基姆(Robert Keim)关于字节序的文章所讨论的那样,该术语指定了数据对象的字节在内存中存储的顺序。处理器可以是小端或大端。使用big-endian处理器时,数据的存储方式是,包含最高有效位的字节具有最低的内存地址。在小端系统中,包含最低有效位的字节首先存储。
图3中所示的示例说明了序列0x01020304的小端和大端存储。


图3.图片由IAR提供。

让我们使用以下代码尝试上一节的并集:

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. int main()
  4. {
  5. union {
  6. struct{
  7. uint8_t   byte1;
  8. uint8_t   byte2;
  9. };
  10. uint16_t    word;
  11. } u1;
  12. u1.byte1 = 0x21;
  13. u1.byte2 = 0x43;
  14. printf("Word is: %#X", u1.word);
  15. return 0;
  16. }

复制代码

运行此代码,我得到以下输出:
词是:0X4321
这表明共享存储空间的第一个字节(“ u1.byte1”)用于存储“ word”变量的最低有效字节(0X21)。换句话说,我用来执行代码的处理器是Little Endian。
如您所见,联合的特定应用程序可能表现出与实现有关的行为。但是,这不是一个严重的问题,因为对于这样的低级编码,我们通常知道处理器的字节序。如果我们不知道这些细节,我们可以使用上面的代码来查找数据在内存中的组织方式。

替代解决方案

除了使用并集,我们还可以使用按位运算符来执行数据打包或拆包。例如,我们可以使用以下代码来组合两个一个字节的变量“ byte3”和“ byte4”,并产生一个单个的两个字节的变量(“ word2”):

  1. word2 = (((uint16_t)   byte3) << 8 ) | ((uint16_t)   byte4);

复制代码

让我们比较一下小端和大端两种情况下这两种解决方案的输出。考虑下面的代码:

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. int main()
  4. {
  5. union {
  6. struct {
  7. uint8_t   byte1;
  8. uint8_t   byte2;
  9. };
  10. uint16_t    word1;
  11. } u1;
  12. u1.byte1 = 0x21;
  13. u1.byte2 = 0x43;
  14. printf("Word1 is: %#X\n", u1.word1);
  15. uint8_t       byte3, byte4;
  16. uint16_t      word2;
  17. byte3 = 0x21;
  18. byte4 = 0x43;
  19. word2 = (((uint16_t) byte3) << 8 ) | ((uint16_t) byte4);
  20. printf("Word2 is: %#X \n", word2);
  21. return 0;
  22. }

复制代码

如果我们针对大型字节序处理器(例如TMS470MF03107)编译此代码,则输出为:

Word1是:0X2143
Word2是:0X2143

但是,如果我们针对像STM32F407IE这样的小端序处理器对其进行编译,则输出将是:

Word1是:0X4321
Word2是:0X2143

尽管基于联合的方法表现出与硬件有关的行为,但是基于移位操作的方法却得到相同的结果,而不管处理器的字节顺序如何。这是由于以下事实:在后一种方法中,我们为变量的名称(“ word2”)分配了一个值,并且编译器负责该设备使用的内存组织。但是,使用基于联合的方法,我们正在更改构成“ word1”变量的字节的值。
尽管基于联合的方法表现出与硬件有关的行为,但它具有更易读和可维护的优点。这就是为什么许多程序员更喜欢在该应用程序中使用联合的原因。

“数据校正”的实际示例  
在使用常见的串行通信协议时,我们可能需要执行数据打包或拆包。考虑一个串行通信协议,该协议在每个通信序列期间发送/接收一个字节的数据。只要我们使用一字节长的变量,就很容易传输数据,但是如果我们有一个任意大小的结构应该通过通信链接怎么办?在这种情况下,我们必须以某种方式将数据对象表示为一字节长的变量数组。一旦获得了字节数组表示,就可以通过通信链接传输字节。然后,在接收器端,我们可以适当地打包它们并重建原始结构。

例如,假设我们需要通过UART通信发送一个浮点变量“ f1”。浮点变量通常占用四个字节。因此,我们可以将以下并集用作提取“ f1”的四个字节的缓冲区:

  1. union {
  2. float    f;
  3. struct {
  4. uint8_t   byte[4];
  5. };
  6. } u1;

复制代码

发送器将变量“ f1”写入联合的float成员。然后,它读取“字节”数组并将字节发送到通信链接。接收器进行相反的操作:它将接收到的数据写入其自己的并集的“字节”数组,并读取并集的float变量作为接收值。我们可以使用这种技术来传输任意大小的数据对象。以下代码可以作为验证此技术的简单测试。

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. int main()
  4. {
  5. float f1=5.5;
  6. union buffer {
  7. float f;
  8. struct {
  9. uint8_t byte[4];
  10. };
  11. };
  12. union buffer buff_Tx;
  13. union buffer buff_Rx;
  14. buff_Tx.f = f1;
  15. buff_Rx.byte[0] = buff_Tx.byte[0];
  16. buff_Rx.byte[1] = buff_Tx.byte[1];
  17. buff_Rx.byte[2] = buff_Tx.byte[2];
  18. buff_Rx.byte[3] = buff_Tx.byte[3];
  19. printf("The received data is: %f", buff_Rx.f);
  20. return 0;
  21. }

复制代码

下面的图4展示了所讨论的技术。请注意,字节是顺序传输的。


图4
结论
联合的原始应用程序创建了互斥变量的共享存储区,但随着时间的流逝,程序员已经广泛使用联合用于完全不同的应用程序:使用联合进行数据打包/拆包。工会的这种特殊应用涉及将值写入工会的一个成员并读取工会的另一个成员。

“数据修剪”或使用联合进行数据打包/拆包可能导致依赖于硬件的行为。但是,它具有更具可读性和可维护性的优点。这就是为什么许多程序员更喜欢在该应用程序中使用联合的原因。当我们有任意大小的数据对象应通过串行通信链接时,“数据修剪”将特别有用。

C语言union用于打包和拆包数据相关推荐

  1. c语言union(c语言union用法)

    C语言-程序运行结果是?main(){unionstu{lon 我的机器+vc6运行的结果是12 我的理解是首先取最长的b的长度,但是b的长度比我的机器字长(32位即4个字节)整数倍要短,则sizeo ...

  2. C语言基础:结构和其他数据形式

    5# C语言基础:结构和其他数据形式 文章目录 Tips1:<img src="https://dl4.weshineapp.com/gif/20170410/5e0f6e9365be ...

  3. 最新综述:用于文本分类的数据增强方法

    ©PaperWeekly 原创 · 作者 | 王馨月 学校 | 四川大学本科生 研究方向 | 自然语言处理 概要 数据增强,即通过转换为机器学习人工创建训练数据,是跨机器学习学科广泛研究的研究领域.它 ...

  4. c语言中,x-y,'105',ab,7f8那个是正确的,C语言程序设计_第三章 数据.ppt

    C语言程序设计_第三章 数据 * 运算符功能 与运算量关系 要求运算量个数 要求运算量类型 运算符优先级别 结合方向 结果的类型 学习运算符应注意 * 基本算术运算符: + - * / % 结合方向: ...

  5. c语言在一组数据中找最大值最小值,用c语言输入一组数据,输出最大最小值,求c语言程序讲解: 输入一组数据,求最大值,最小值,和,平均...

    问题标题 用c语言输入一组数据,输出最大最小值,求c语言程序讲解: 输入一组数据,求最大值,最小值,和,平均 2019-7-3来自ip:18.156.193.207的网友咨询 浏览量:318 手机版 ...

  6. Go语言编程笔记16:存储数据

    Go语言编程笔记16:存储数据 图源:wallpapercave.com 几乎任何程序都绕不开读写数据,只不过具体的数据存储介质和方式有所不同.本篇文章将从多种数据存储方式进行探讨各种存储方式如何实现 ...

  7. C语言union总结

    C语言union总结 最近在工作中看到前辈写了串代码,使用union,加深了对C语言的认识,感叹C语言的博大精深! 需要实现的功能: 从模块有5个按键,每个按键有5种状态.主模块与从模块串口相连.当从 ...

  8. 大数据要掌握哪些语言?怎样才能学好大数据?

    大数据是近五年兴起的行业,发展迅速,很多技术经过这些年的迭代也变得比较成熟了,同时新的东西也不断涌现,想要保持自己竞争力的唯一办法就是不断学习.但是,大数据需要学习什么? 01 思维导图 我还是要推荐 ...

  9. R语言心得说:R语言之xlsx包读写Excel数据

    R语言心得说:R语言之xlsx包读写Excel数据 感谢Adrian A. Drǎgulescu发布的xlsx包 工具准备 [基础]简单读取excel文件数据 [基础]简单写入数据到excel文件 [ ...

最新文章

  1. jsp 插入mysql乱码_JSP MySQL插入数据时出现中文乱码问题的解决方法
  2. zabbix使用zabbix 数据库做数据分表
  3. 密度聚类算法DBSCAN实战及可视化分析
  4. u盘安装ubuntu10.04 、11.04 server
  5. c语言联合体作用,C语言 联合体(Unions)
  6. mysql常用命令,mysql语法,mysql登陆、创建数据库、创建用户、更改密码、为用户授权...
  7. 一场虚拟的鄂尔多斯婚礼 (图)
  8. anychart说明文档
  9. java迭代器逆序_迭代器
  10. 田园主义创始人发布致歉声明:测算失误导致热量差异
  11. 哪吒之魔童降世视听语言影评_《哪吒之魔童降世》观后感精彩影评5篇450字
  12. 证:单层感知机不能表示异或逻辑
  13. 三菱q系列plc连接电脑步骤_SERVER和三菱Q系列PLC通讯设置步骤 SERVER和三菱Q系列PLC通讯设置步骤...
  14. 制图软件cad-深度了解Mechanical 工具组合
  15. 喧喧发布 2.5.3 版本,主要提升系统稳定性,优化交互体验
  16. 请问 S2S 和C2S 是什么样的模式,两者之间的区别又是什么样的?
  17. 分布式 常见的容错机制
  18. matlab找不到exe文件,如何修复pyinstaller在从scrip创建exe文件时找不到matlab文件的问题...
  19. Android经典的大牛博客推荐
  20. 当世事再没完美可远在岁月如歌中找你

热门文章

  1. isEmpty VS isBlank
  2. Python实现深度学习系列之【正向传播和反向传播】
  3. QT 对QString字符串的操作
  4. [游戏]_纪念一下dnf,安心准备考研
  5. EditPlus3.4 注册码
  6. 程序人生 | 韭菜的股市沉浮
  7. 为了对抗这种感染3亿人的病毒,他们靠人体实验堆出了这种疫苗
  8. Office Word页眉页脚设置、显示分节符、页眉“与上一节相同”的问题
  9. NoVNC—以Web方式交付VNC远程连接
  10. 源代码加密新兴方案:环境加密