memcpy to union并没有像预期的那样工作

时间:2017-09-15 11:29:31

标签: c struct embedded unions memcpy

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

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

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没有采用正确的值。实际上,它所需要的值是有效载荷[8]而不是有效载荷[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。

非常感谢任何帮助或指示。

2 个答案:

答案 0 :(得分:0)

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

幸运的是,编译器支持__attribute__((__packed__))

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

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

答案 1 :(得分:0)

使用“packed”结构时要小心 - 根据目标cpu的详细信息,很容易获得非对齐访问,这可能是非法的或仅仅是低效的。考虑重新安排结构类型以避免需要“打包”。

当您使用结构体进行此类操作时,我建议在编译器上使用“-Wpadded”。这将让编译器告诉您何时添加了填充结构。如果您期望填充,请明确添加(例如“uint8_t dummy [3];”)。

还要慷慨地使用_Static_assert来检查结构是否符合预期。你的目标是不匹配产生编译时错误,而不是等待测试和调试。