将消息解析成结构体

3

我在将一条消息解析成结构体时遇到了一些问题。我知道这条消息被分成了字节,并且这条消息始终是11个字节长的。有人告诉我,一个非常优雅的解决方案是将传入的缓冲区强制转换为字节的结构体来解析数据。我的问题是如何执行该功能。我听说通过将缓冲区强制转换为char *,您应该能够在一两行内执行转换。

typedef struct tStateMsg {
    uint8_t reportID;
    uint8_t ctrlName1;
    uint8_t State1;
    uint8_t ctrlName2;
    uint8_t State2;
    uint8_t ctrlName3;
    uint8_t State3;
    uint8_t ctrlName4;
    uint8_t State4;
    uint8_t ctrlName5;
    uint8_t State5;
} StateMsg;

void SetState(uint8 msgBuffer[], uint8 bufferSize) //BufferSize is always 11
{
    //Parse the message to the struct.
}

我知道需要额外的错误检查来确保消息始终为11个字节,并且所有我期望的数据都存在,但是我现在会暂时忽略它。
谢谢你的帮助!

如果这里有填充,我会感到惊讶,但请注意编译器允许在类成员之间添加填充。这意味着 sizeof(tStateMsg) >= 11 - NathanOliver
1
C还是C++?它们不是同一种语言。我相当确定dbush的答案在C++中是非法的。标记其中一个。不要同时使用两个。 - asu
2
你可能想看一下Eric Raymond关于结构体打包的文章 - Rob K
不,@Asu,dbush的答案在C++中并不违法。它是完全合法的。 - Rob K
1个回答

3

这是通过将缓冲区(实际上是缓冲区开头的地址)转换为指向结构体的指针来完成的:

StateMsg *msg = (StateMsg *)msgBuffer;

然后你可以引用 `struct` 成员。
需要注意的一件事是对齐方式。如果缓冲区是通过 `malloc` 创建的,那么该缓冲区将针对任何数据类型进行正确的对齐。但是,如果缓冲区是本地数组,则可能未对齐在适合 `StateMsg` 的边界上。
因此,请确保使用经过 `malloc` 分配的缓冲区来解决地址对齐问题。
至于 `struct` 元素之间的填充,编译器可以在任何地方插入填充。为确保该结构按照您期望的方式布局,您需要声明所需打包的 `struct`。在 gcc 中,您可以使用 `#pragma packed` 来实现这一点。
然而,在实践中,只要将每个元素放置在适当的偏移量处(即在 2 字节的偏移量处放置 2 字节类型,在 4 字节的偏移量处放置 4 字节类型),则应该得到预期布局的结构。在您的特定示例中,所有字段都是 1 字节,因此除了最后一个字段外,不应有任何填充。
如需更多详细信息,请查看 此优秀的有关结构体打包的文章(感谢 Rob K 在评论中提供此信息)。

5
请注意,编译器允许在成员之间插入填充字节,如对齐。为了实现您的解决方案,结构必须是紧凑的。许多编译器具有指示结构体紧凑排列的指令或选项。 - Thomas Matthews
1
在实际应用中,对于这个结构体来说,由于都是uint8_t类型,编译器针对32位代码目标只会在结构体末尾添加1字节以使其达到32位对齐,从而使结构体的大小为3个32位字,或者如果是针对64位代码目标,将在结构体末尾添加5字节填充以使其达到64位对齐,从而使结构体的大小为2个64位字。(参考:http://www.catb.org/esr/structure-packing/#_alignment_requirements) - Rob K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接