哪种方法可以摆脱严格的别名警告?
代码是:
uint8_t msg[3];
int retval;
msg[0] = (uint8_t) INT_READ_EVENT;
*((uint16_t *) &msg[1]) = bytesToRead;
retval = write(intPipe[1], msg, sizeof(msg));
bytesToRead作为uint16_t值传递。
另一方面,在处理剩余数据之前,有一个读取接收单个字节以识别事件类型(在本例中为INT_READ_EVENT)。
有没有快速,简单的方法来摆脱警告:
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
*((uint16_t *) &msg[1]) = bytesToRead;
^
答案 0 :(得分:3)
像这样,因为它迫使你决定16位值应该表示为字节序:
// Little-endian.
msg[1] = bytesToRead & 0xff;
msg[2] = (bytesToRead >> 8) & 0xff;
对于big-endian,颠倒右边的顺序。
答案 1 :(得分:3)
听起来你正在解码某种协议,所以一种方法是声明一个与协议数据相对应的结构:
typedef struct
{
uint8_t something;
uint16_t bytes_to_read;
} prot_data_t;
然后为方便起见,为了避免混淆问题,你可以把它放在一个联合中:
typedef union
{
prot_data_t named_data;
uint8_t raw_data [sizeof(prot_data_t)];
} protocol_t;
现在,您可以通过raw_data
逐字节传输/接收数据,但可以通过named_data
访问不同的值。
需要考虑的一些事项:
请注意,此代码以及不使用位移的任何解决方案都将依赖于字节序。
像这样的代码可能容易受到对齐和struct / union填充问题的影响。为了获得最大的可移植性,除非您将它们与某些序列化/反序列化例程结合使用,否则不建议使用结构/联合。答案 2 :(得分:2)
那个警告是有原因的。如果msg
在16位边界上开始,则msg[1]
不会。因此,如果您尝试从该字段的地址开始写入16位值,则可能会触发陷阱,导致无效写入内存并导致崩溃。
您应该一次写一个字节。假设你想要大端格式的字节(也称为网络字节顺序),你可以这样做:
msg[1] = (uint8_t)(bytesToRead >> 8);
msg[2] = (uint8_t)(bytesToRead);
答案 3 :(得分:1)
您可以定义一个结构来执行此操作,而不是使用char缓冲区。
struct message
{
uint8_t code;
uint16_t payload;
};
struct message msg;
msg.code = INT_READ_EVENT;
msg.value = bytesToRead;
多平台/可移植代码必须考虑对齐和字节顺序。
如果还想以原始模式访问struct的数据,您可以:
union message_with_raw
{
struct message msg;
uint8_t rawdata[sizeof(struct message)];
}