龙空技术网

每天学习一点:用于打包和解包数据的C语言中的联合(Union)

万物云联网 481

前言:

如今朋友们对“union”大致比较注重,兄弟们都想要剖析一些“union”的相关资讯。那么小编同时在网摘上汇集了一些对于“union””的相关知识,希望咱们能喜欢,朋友们一起来学习一下吧!

在前一篇文章中,我们讨论了联合(Union)的原始应用,联合(Union)数据结构一直在为互斥变量创建共享内存区域。但是,随着时间的推移,程序员已广泛使用联合(Union)用于完全不同的应用程序:从较大的数据对象中提取较小的数据部分。在本文中,我们将更详细地介绍联合(Union)的这个特定应用。

使用联合(Union)包装/拆包数据

联合(Union)的数据成员存储在共享内存区域中。这是允许我们为联合(Union)找到有趣的程序应用的关键功能。

考虑下面的联合(Union):

此联合(Union)中有两个成员:第一个成员“word”是一个双字节变量。第二个成员是两个单字节变量的结构。为联合(Union)分配的两个字节在其两个成员之间共享。

分配的内存空间可以如下面的图1所示:

图1

虽然“word”变量指的是整个分配的存储空间,但“byte1”和“byte2”变量指的是构成“word”变量的单字节区域。我们如何使用此功能?假设您有两个单字节变量“x”和“y”,它们应该组合起来产生一个双字节变量。

在这种情况下,您可以使用上面的联合(Union)并将“x”和“y”分配给结构成员,如下所示:

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

图2

上面的例子显示了使用联合(Union)将两个单字节变量打包成一个双字节变量。我们也可以反过来:将一个双字节值写入“word”并通过读取“x”和“y”变量将其解压缩为两个单字节变量。将值写入联合(Union)的一个成员并读取其中的另一个成员有时被称为“数据压缩(data punning)”。

处理器的字节顺序

使用联合(Union)打包/解包数据时,我们需要注意处理器的字节顺序。正如Robert Keim关于字节序的文章中所讨论的,该术语指定了数据对象的字节存储在内存中的顺序。处理器可以是小端或大端。对于大端处理器,数据的存储方式使包含最高有效位的字节具有最低的存储器地址。在小端系统中,首先存储包含最低有效位的字节。

图3中描述的示例说明了序列0x01020304的小端和大端存储。

图3

图3.图片由IAR提供。

让我们使用以下代码来试验上一节的联合(Union):

运行此代码,我得到以下输出:

字节是:0X4321

这表明共享存储空间的第一个字节(“u1.byte1”)用于存储“word”变量的最低有效字节(0X21)。换句话说,我用来执行代码的处理器是小端。

如您所见,这个联合(Union)的特定应用程序可以表现出依赖于实现的行为。但是,这不应该是一个严重的问题,因为对于这种低级编码,我们通常知道处理器的字节顺序。如果我们不知道这些细节,我们可以使用上面的代码来了解数据在内存中的组织方式。

替代方案

我们也可以使用按位运算符来执行数据的打包或解包,而不是使用联合(Union)。例如,我们可以使用以下代码组合两个单字节变量“byte3”和“byte4”,并生成一个双字节变量(“word2”):

让我们比较这两个解决方案在小端和大端情况下的输出。请考虑以下代码:

如果我们为大端处理器(如TMS470MF03107)编译此代码,则输出将为:

Word1是:0X2143

Word2是:0X2143

但是,如果我们为一个小端序处理器(如STM32F407IE)编译它,输出将是:

Word1是:0X4321

Word2是:0X2143

虽然基于联合(Union)的方法表现出依赖于硬件的行为,但是基于移位操作的方法导致相同的结果,而不管处理器的字节顺序如何。这是因为,采用后一种方法,我们为变量名称(“word2”)赋值,编译器负责设备使用的内存组织。但是,使用基于联合(Union)的方法,我们正在更改构造“word1”变量的字节值。

尽管基于联合(Union)的方法表现出依赖于硬件的行为,但它具有更易读和可维护的优点。这就是为什么许多程序员喜欢在这个应用程序中使用联合(Union)的原因。

“数据压缩(Data Punning)”的实例

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

例如,假设我们需要通过UART通信发送一个浮点变量“f1”。 float变量通常占用四个字节。因此,我们可以使用以下联合(Union)作为缓冲区来提取“f1”的四个字节:

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

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

图4

结论

虽然联合(Union)的原始应用正在为互斥变量创建共享内存区域,但随着时间的推移,程序员已经广泛使用了联合(Union)用于完全不同的应用程序中:使用联合(Union)进行数据打包/解包。联合(Union)的这种特殊应用涉及将值写入联合(Union)的一个成员并读取其中的另一个成员。

“数据压缩(data punning)”或使用联合(Union)进行数据打包/解包可能导致依赖于硬件的行为。但是,它具有更易读和可维护的优点。这就是为什么许多程序员喜欢在应用程序中使用联合(Union)的原因。当我们有一个应该通过串行通信链路的任意大小的数据对象时,“数据压缩(data punning)”会特别有用。

标签: #union #c语言union的用法