如果“设备B”需要不断获取所有这些信息,我们可以将所有这些变量存储在结构中,并将该结构发送到“设备B”。结构大小将至少等于这些变量的大小之和,即9个字节。
因此,每次“设备A”与“设备B”对话时,都需要通过两个设备之间的通信链路传输9字节的数据帧。图2描绘了“设备A”用来存储变量和需要通过通信链接的数据帧的结构。
图2
但是,让我们考虑另一种情况,我们仅偶尔需要发送状态信息。另外,假设没有必要在给定时间同时获取位置和速度信息。换句话说,有时我们仅发送位置,有时仅发送速度,有时仅发送状态信息。在这种情况下,将信息存储为9字节结构并通过通信链接进行传输似乎不是一个好主意。
状态信息只能由三个字节表示。对于位置和速度,我们分别只需要四个和两个字节。因此,“设备A”在一次传输中需要发送的最大字节数为4,因此,我们仅需要四个字节的内存即可存储此信息。这四个字节的内存空间将在我们的三种消息类型之间共享(请参见图3)。
此外,请注意,通过通信链路传递的数据帧的长度从九个字节减少到四个字节。
图3
总而言之,如果我们的程序具有互斥的变量,我们可以将它们存储在共享的内存区域中,以保留宝贵的内存空间。这可能很重要,尤其是在内存受限的嵌入式系统中。在这种情况下,我们可以使用联合创建所需的共享内存空间。
上面的示例表明,使用联合来处理互斥变量也可以帮助我们节省通信带宽。节省通信带宽有时甚至比节省内存更为重要。
对消息包使用联合
让我们看看如何使用联合存储上面示例的变量。我们有三种不同的消息类型:状态,位置和速度。我们可以为状态和位置消息的变量创建一个结构(以便将这些消息的变量分组并作为单个数据对象进行操作)。
以下结构用于此目的:
- struct {
- uint8_t power;
- unit8_t op_mode;
- uint8_t temp;
- } status;
- struct {
- uint16_t x_pos;
- unit16_t y_pos;
- } position;
复制代码
现在,我们可以将这些结构与“ vel”变量一起放在一个并集中:
- union {
- struct {
- uint8_t power;
- unit8_t op_mode;
- uint8_t temp;
- } status;
- struct {
- uint16_t x_pos;
- unit16_t y_pos;
- } position;
- uint16_t vel;
- } msg_union;
复制代码
上面的代码指定了联合模板,并创建了该模板的变量(名为“ msg_union”)。在该联合内部,有两个结构(“状态”和“位置”)和一个两个字节的变量(“ vel”)。此联合的大小将等于其最大成员的大小,即“位置”结构,该结构占用四个字节的内存。此存储空间在“状态”,“位置”和“ vel”变量之间共享。
如何跟踪结构体活跃成员
我们可以使用上述联合的共享内存空间来存储我们的变量;但是,仍然存在一个问题:接收方应如何确定已发送哪种类型的消息?接收者需要识别消息类型才能成功解释接收到的信息。例如,如果我们发送“位置”消息,则接收到的数据的所有四个字节都很重要,但是对于“速度”消息,仅应使用接收到的字节中的两个。
要解决此问题,我们需要将联合与另一个变量关联,例如“ msg_type”,该变量指示消息类型(或最后写入的联合成员)。结合有离散值(表示该联盟的活动成员)的联合称为“区分联合”或“标记联合”。
关于“ msg_type”变量的数据类型,我们可以使用C语言的枚举数据类型来创建符号常量。但是,我们将使用字符来指定消息类型,只是为了使事情尽可能简单:
- struct {
- uint8_t msg_type;
- union {
- struct {
- uint8_t power;
- unit8_t op_mode;
- uint8_t temp;
- } status;
- struct {
- uint16_t x_pos;
- unit16_t y_pos;
- } position;
- uint16_t vel;
- } msg_union;
- } message;
复制代码
我们可以为“ msg_type”变量考虑三个可能的值:“ s”表示“状态”消息,“ p”表示“位置”消息,“ v”表示“速度”消息。现在,我们可以将“消息”结构发送到“设备B”,并使用“ msg_type”变量的值作为消息类型的指示符。例如,如果接收到的“ msg_type”的值为“ p”,则“设备B”将知道共享内存空间包含两个2字节变量。
注意,由于我们需要传递“ msg_type”变量,因此必须在通过通信链接发送的数据帧中添加另一个字节。还请注意,使用此解决方案,接收者无需提前知道传入的是哪种消息。
替代解决方案:动态内存分配
我们看到,并集使我们可以声明一个共享内存区域,以节省内存空间和通信带宽。但是,还有另一种存储互斥变量的方法,例如上面的示例。第二种解决方案使用动态内存分配来存储每种消息类型的变量。
同样,我们将需要有一个变量“ msg_type”来指定通信链路的发送器和接收器端的消息类型。例如,如果“设备A”需要发送位置消息,它将“ msg_type”设置为“ p”并分配四个字节的存储空间来存储“ x_pos”和“ y_pos”变量。接收器将检查“ msg_type”的值,并根据其值创建适当的存储空间以存储和解释传入的数据帧。
就内存使用而言,动态内存的使用会更有效,因为我们为每种消息类型分配的空间恰到好处。基于联合的解决方案并非如此。在那里,我们有四个字节的共享内存来存储所有三种消息类型,尽管“状态”消息和“速度”消息分别仅需要三个字节和两个字节。但是,动态内存分配可能会变慢,并且程序员需要包含释放分配的内存的代码。因此,程序员通常更喜欢使用基于联合的解决方案。