本文总共3163个字,阅读需11分钟,全文加载时间:2.188s,本站综合其他专栏收录该内容! 字体大小:

文章导读:了解嵌入式C语言中称为联合(unions)的数据对象。 嵌入式C中结构(Structure)与联合(Union)的区别 在本系列的前一篇文章中,我们讨论了嵌入式C中的结构允许我们对不同数据类型的变量进行分组,并将它们作为单……各位看官请向下阅读:

了解嵌入式C语言中称为联合(unions)的数据对象。

嵌入式C中结构(Structure)与联合(Union)的区别

在本系列的前一篇文章中,我们讨论了嵌入式C中的结构允许我们对不同数据类型的变量进行分组,并将它们作为单个数据对象处理。

除了结构(Structure)之外,C语言还支持另一种称为联合(Union)的数据结构,它可以将不同的数据类型组合为单个数据对象。 本文将提供有关联合(Union)的一些基本信息。 我们首先看一下声明一个union的介绍性例子,然后我们将研究这个数据对象的一个重要应用。

介绍性实例

声明一个联合(Union)很像声明一个结构(Structure)。 我们只需要用“union”替换关键字“struct”。 请考虑以下示例代码:

这指定了一个具有两个成员变量的模板:“c”(占用一个字节)和“i”(占用四个字节)。

现在,我们可以创建此联合模板的变量:

使用成员运算符(.),我们可以访问“u1”联合的成员。例如,以下代码将10分配给上述联合的第二个成员,并将“c”的值复制到“m”变量中(必须是uint8_t类型):

将分配多少内存空间来存储“u1”变量?虽然结构(Structure)的大小至少与其成员大小的总和一样大,但联合(Union)的大小等于其最大变量的大小。分配给联合(Union)的内存空间将在所有联合(Union)成员之间共享。在上面的例子中,“u1”的大小等于uint32_t的大小,即四个字节。该存储空间在“i”和“c”之间共享。因此,为这两个成员之一分配值将更改另一个成员的值。

您可能想知道,“使用相同的内存空间存储多个变量有什么意义?这个功能有什么应用吗?”我们将在下一节中探讨这个问题。

我们需要共享内存空间吗?

让我们看一个联合(Union)可以成为有用数据对象的例子。假设,如下面的图1所示,系统中有两个设备需要相互通信。

图1

“设备A”应将状态,速度和位置信息发送到“设备B”。状态信息由三个变量组成,用于指示电池电量,操作模式和环境温度。该位置由两个显示x轴和y轴位置的变量表示。最后,速度由单个变量表示。假设这些变量的大小如下表所示。

示例表格1

如果“设备B”经常需要包含这些信息的每一部分,我们可以将所有这些变量存储在一个结构(Structure)中并将结构(Structure)发送到“设备B”。结构(Structure)大小至少与这些变量的大小之和一样大,即9个字节。

因此,每次“设备A”与“设备B”对话时,它需要通过两个设备之间的通信链路传输9个字节数据帧。图2描绘了“设备A”用于存储变量和需要通过通信链路的数据帧的结构(Structure)。

图2

但是,让我们考虑一个不同的场景,我们偶尔只需要发送状态信息。另外,假设在给定时间没有必要同时具有位置和速度信息。换句话说,有时我们只发送位置信息,有时我们只发送速度信息,有时我们只发送状态信息。在这种情况下,将信息存储在九字节结构中并通过通信链路传输它似乎不是一个好主意。

状态信息只能用三个字节表示;对于位置和速度,我们分别只需要四个和两个字节。因此,“Device A”在一次传输中需要发送的最大字节数是4,因此,我们只需要4个字节的内存来存储该信息。这个四字节的存储空间将在我们的三种消息类型之间共享(参见图3)。

另外,请注意,通过通信链路传输的数据帧的长度从9个字节减少到4个字节。

图3

总而言之,如果我们的程序具有互斥的变量,我们可以将它们存储在共享的内存区域中,以保留宝贵的内存空间。这可能很重要,尤其是在内存受限的嵌入式系统环境中。在这种情况下,我们可以使用联合(Union)来创建所需的共享内存空间。

上面的例子表明,使用联合(Union)来处理互斥变量也可以帮助我们节省通信带宽。节省通信带宽有时甚至比节省内存更重要。

联合(Union)用于消息包中

让我们看看我们如何使用联合(Union)来存储上面例子的变量。 我们有三种不同的消息类型:状态,位置和速度。 我们可以为状态和位置消息的变量创建一个结构(Structure)(以便将这些消息的变量分组并作为单个数据对象进行操作)。

以下结构(Structure)用于此目的:

现在,我们可以将这些结构(Structure)与“vel”变量一起放在一个联合(Union)中:

上面的代码指定了一个联合(Union)模板,并创建了该模板的一个变量(名为“msg_union”)。在这个联合(Union)内部,有两种结构(Structure)(“状态”和“位置”)和一个双字节变量(“vel”)。此并集的大小将等于其最大成员的大小,即“位置”结构,它占用四个字节的内存。该“存储空间”在“status”,“position”和“vel”变量之间共享。

如何跟踪联合活跃成员(Union Active Member)

我们可以使用上面联合(Union)的共享内存空间来存储我们的变量;但是,还有一个问题:接收方应该如何确定发送了哪种类型的消息?接收器需要识别消息类型以能够成功地解释所接收的信息。例如,如果我们发送“位置”消息,则接收数据的所有四个字节都很重要,但对于“速度”消息,只应使用两个接收字节。

要解决这个问题,我们需要将我们的union与另一个变量相关联,比如“msg_type”,它指示消息类型(或最后写入的union成员)。与表示联合(Union)的活动成员的离散值配对的联合称为“区分联合(discriminated union)”或“标记联合(tagged union)”。

关于“msg_type”变量的数据类型,我们可以使用C语言的枚举(enumeration)数据类型来创建符号常量。但是,我们将使用一个字符来指定消息类型,只是为了使事情尽可能简单:

我们可以考虑“msg_type”变量的三个可能值:“s”表示“状态”消息,“p”表示“位置”消息,“v”表示“速度”消息。现在,我们可以将“消息”结构发送到“设备B”,并使用“msg_type”变量的值作为消息类型的指示符。例如,如果接收到的“msg_type”的值为“p”,则“设备B”将知道共享存储空间包含两个2字节变量。

请注意,我们必须向通过通信链路发送的数据帧添加另一个字节,因为我们需要传输“msg_type”变量。另请注意,使用此解决方案,接收器不需要提前知道正在发送什么类型的消息。

替代解决方案:动态内存分配

我们看到联合(Union)允许我们声明共享内存区域以节省内存空间和通信带宽。但是,存在另一种存储互斥变量的方法,例如上例中的变量。第二种解决方案使用动态内存分配来存储每种消息类型的变量。

同样,我们需要一个变量“msg_type”来指定通信链路的发送器和接收器端的消息类型。例如,如果“Device A”需要发送位置消息,它会将“msg_type”设置为“p”并分配四个字节的内存空间来存储“x_pos”和“y_pos”变量。接收器将检查“msg_type”的值,并根据其值创建适当的存储空间,用于存储和解释传入的数据帧。

使用动态内存在内存使用方面可以更有效,因为我们为每种消息类型分配了足够的空间。基于联合的解决方案并非如此。在那里,我们有四个字节的共享内存来存储所有三种消息类型,尽管“状态”和“速度”消息分别只需要三个和两个字节。但是,动态内存分配可能较慢,程序员需要包含释放分配内存的代码。这就是程序员通常喜欢使用基于联合的解决方案的原因。

下一步:联合(Union)的应用

似乎联合(Union)的最初目的是为互斥变量创建共享内存区域。但是,工联合(Union)也被广泛用于从较大的数据对象中提取较小的数据部分。

本系列的下一篇文章将重点介绍联合(Union)的这种应用,这在嵌入式应用程序中尤为重要。

以上内容由优质教程资源合作伙伴 “鲸鱼办公” 整理编辑,如果对您有帮助欢迎转发分享!

你可能对这些文章感兴趣:

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注