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 来检查您的结构是否符合您的预期大小。您的目标是让不匹配产生编译时错误,而不是等待测试和调试。
我正在开发一些嵌入式软件,并试图让事情变得更加灵活。我希望能够做的一件事是更改结构,并在整个应用程序的其余部分更改数据处理方式。
我注意到我可以将一些 数据从有效载荷复制到联合中,但由于某种原因不能全部复制。这是我正在做的事情:
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 来检查您的结构是否符合您的预期大小。您的目标是让不匹配产生编译时错误,而不是等待测试和调试。