c - 如何处理严格的别名警告

时间:2016-07-01 12:54:05

标签: c strict-aliasing type-punning

哪种方法可以摆脱严格的别名警告?

代码是:

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;
    ^

4 个答案:

答案 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)];
}