memcpy to union 没有按预期工作

memcpy to union isn't working as expected

我正在开发一些嵌入式软件,并试图让事情变得更加灵活。我希望能够做的一件事是更改结构,并在整个应用程序的其余部分更改数据处理方式。

我注意到我可以将一些 数据从有效载荷复制到联合中,但由于某种原因不能全部复制。这是我正在做的事情:

union ConcentratorPacket {
 struct PacketHeader header;
 struct NetworkJoinReqPacket networkJoinReqPacket;
};

static union ConcentratorPacket latestRxPacket;

其中:

struct PacketHeader {
 uint32_t sourceAddress;
 uint8_t packetType; 
};

struct NetworkJoinReqPacket{
 struct PacketHeader header;
 uint8_t maxDataLen;
};

稍后我想将数据从接收到的数据包移动到这个联合内的相关结构中,所以我想这样做:

memcpy(&latestRxPacket.networkJoinReqPacket, rxPacket->payload, sizeof(latestRxPacket.networkJoinReqPacket));

其中 rxPacket->payload 是 uint8_t 的数组,按正确顺序交付。

我看到的是 packetHeader 使用此方法很好地填充,但 maxDataLen 没有采用正确的值。事实上它取的值是 payload[8] 而不是 payload[5]。

我发现解决此问题的唯一方法是直接为 maxDataLen 赋值,但如果结构因任何原因发生更改,则需要在每个地方进行更改,因此 memcpy 比这个更可取:

 memcpy(&latestRxPacket.networkJoinReqPacket.header, rxPacket->payload, sizeof(latestRxPacket.networkJoinReqPacket.header));
 latestRxPacket.networkJoinReqPacket.maxDataLen = rxPacket->payload[5];

我认为我所看到的表明 memcpy 将 maxDataLen 视为 uint32,右对齐,但我不知道如何避免这种情况。

这很奇怪,因为我在其他地方做了类似的事情并且它工作正常,但唯一的区别是与 maxDataLen 等效的是 uint32,而不是 uint8。

如有任何帮助或指导,我们将不胜感激。

正如一些程序员指出的那样,它与 struct padding/packing 有关。似乎默认情况下,我在 Code Composer Studio 中使用的 TI 编译器填充结构以将它们与 32 位内存对齐。

还好__attribute__((__packed__))编译器支持

只需更改 PacketHeader 的定义即可解决问题:

struct __attribute__((__packed__)) PacketHeader {
  uint32_t sourceAddress;
  uint8_t packetType;
};

小心使用 "packed" 结构 - 很容易获得未对齐的访问,这可能是非法的或仅仅是低效的,具体取决于目标 cpu 的详细信息。考虑重新安排结构类型以避免需要 "packed".

当你为这类事情使用结构时,我建议在编译器上使用“-Wpadded”。这将使编译器告诉您何时将填充添加到结构中。如果您需要填充,请显式添加(例如 "uint8_t dummy[3];")。

也可以慷慨地使用 _Static_assert 来检查您的结构是否符合您的预期大小。您的目标是让不匹配产生编译时错误,而不是等待测试和调试。