【发布时间】:2014-03-27 09:22:40
【问题描述】:
前几天我的一个朋友在 Facebook 上发布了这个问题,而我这辈子都想不通。他正在使用 cubesat 协议编写客户端和服务器。由于某种原因,当他将协议结构的数据成员强制转换为指针时,他的数据似乎被破坏了。
客户端代码sn-p:
uint32_t data[3] = { 1234U, 5678U, 9101U };
memcpy(packet->data32, data, sizeof(data));
packet->length = sizeof(data);
csp_send(connection, packet, 1000);
服务器代码sn-p:
uint32_t *data = (uint32_t *)(packet->data32);
printf("Packet received on %i: %u\r\n", PORT, data[0]);
printf("Packet received on %i: %u\r\n", PORT, data[1]);
printf("Packet received on %i: %u\r\n", PORT, data[2]);
printf("Packet received on %i: %u, %u, %u\r\n", PORT, data[0], data[1], data[2]);
输出此代码产生的结果:
Packet received on 15: 2182284498
Packet received on 15: 5678
Packet received on 15: 9101
Packet received on 15: 80904723, 372113408, 596443136
输出此代码的普通读者会期望:
Packet received on 15: 1234
Packet received on 15: 5678
Packet received on 15: 9101
Packet received on 15: 1234, 5678, 9101
经过一番摆弄后,他告诉我,如果他不将结构的 data32 成员转换为 uint32_t*,他会得到正确的输出。
根据我自己的研究,packet 的类型为csp_packet_t,其定义为:
typedef struct __attribute__((__packed__)) {
uint8_t padding[CSP_PADDING_BYTES]; // Interface dependent padding
uint16_t length; // Length field must be just before CSP ID
csp_id_t id; // CSP id must be just before data
union {
uint8_t data[0]; // This just points to the rest of the buffer, without a size indication.
uint16_t data16[0]; // The data 16 and 32 types makes it easy to reference an integer (properly aligned)
uint32_t data32[0]; // - without the compiler warning about strict aliasing rules.
};
} csp_packet_t;
完整的头文件是here。
这是 GNU C,所以zero-length arrays are allowed。
我不知道两边架构的字长或字节序。
所以,简单地说 - 这里发生了什么?为什么演员阵容很重要?
【问题讨论】:
-
澄清一下。如果你只是用
uint32_t *data = packet->data32;替换你原来的服务器第一行,那么它就开始工作了吗?? -
不,他完全删除了该行并将
data[...]替换为packet->data32[...],现在我这么说,让我几乎可以肯定这是因为联合未对齐。 -
您的意思是编译器(和标准 C?)保证对
struct->arr[k]的非对齐访问,但不能直接对指针间接访问。那将是合理的行为。 -
是的,正是如此——请参阅下面我自己的答案。
-
标准 C 从不保证任何情况下的非对齐访问
标签: c gcc struct cubesat-protocol