修复取消引用类型惩罚指针将破坏严格别名

我正在尝试使用GCC编译特定程序时修复两个警告。 警告是:

警告:解除引用类型惩罚指针将破坏严格别名规则[-Wstrict-aliasing]

而这两个罪魁祸首是:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf)); 

 *((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset); 

incoming_bufoutgoing_buf定义如下:

 char incoming_buf[LIBIRC_DCC_BUFFER_SIZE]; char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE]; 

这似乎与我一直在研究的那个警告的其他例子略有不同。 我宁愿修复问题而不是禁用严格别名检查。

有很多建议要使用联合 – 这个案例可能是一个合适的联合体?

首先,让我们来看看你为什么会得到别名违规警告。

别名规则只是说您只能通过自己的类型,签名/无符号变体类型或字符类型( charsigned charunsigned char )访问对象。

C表示违反别名规则会调用未定义的行为( 所以不要! )。

在你的程序的这一行:

 unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf)); 

虽然incoming_buf数组的元素是char类型,但是您将它们作为unsigned int访问。 实际上,表达式*((unsigned int*)dcc->incoming_buf)中的解除引用运算符的结果是unsigned int类型。

这违反了别名规则,因为您只有权访问incoming_buf数组的元素(参见上面的规则摘要!) charsigned charunsigned char

请注意,您在第二个罪魁祸首中存在完全相同的别名问题:

 *((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset); 

您可以通过unsigned int访问outgoing_bufchar元素,因此它是一个别名冲突。

提出的解决方案

要解决您的问题,您可以尝试直接在要访问的类型中定义数组的元素:

 unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)]; unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)]; 

(顺便说一下unsigned int的宽度是实现定义的,所以你应该考虑使用uint32_t如果你的程序假设unsigned int是32位)。

这样,您可以通过类型char访问元素,从而在不违反别名规则的情况下将unsigned int对象存储在数组中,如下所示:

 *((char *) outgoing_buf) = expr_of_type_char; 

要么

 char_lvalue = *((char *) incoming_buf); 

编辑:

我完全重写了我的答案,特别是我解释了为什么程序从编译器获得别名警告。

要解决这个问题, 请不要使用双关语和别名 ! 读取类型T的唯一“正确”方法是分配类型T并在需要时填充其表示:

 uint32_t n; memcpy(&n, dcc->incoming_buf, 4); 

简而言之:如果你想要一个整数,你需要做一个整数。 没有办法以语言宽容的方式欺骗​​它。

允许的唯一指针转换(通常用于I / O)是将类型T 的现有变量的地址视为char* ,或者更确切地说,作为指向char数组的第一个元素的指针尺寸sizeof(T)

 union { const unsigned int * int_val_p; const char* buf; } xyz; xyz.buf = dcc->incoming_buf; unsigned int received_size = ntohl(*(xyz.int_val_p)); 

简化说明1. c ++标准规定您应该自己尝试对齐数据,g ++会在这个主题上产生警告。 2.只有在完全理解架构/系统和代码内部的数据对齐时才应该尝试它(例如上面的代码在Intel 32/64上是肯定的;对齐1; Win / Linux / Bsd / Mac) 3.使用上述代码的唯一实际原因是避免编译器警告,WHEN和IF你知道你在做什么

如果我可以,恕我直言,对于这种情况,问题是ntohl和htonl及相关functionAPI的设计。 它们不应该用数字返回写成数字参数。 (是的,我理解宏优化点)它们应该被设计为’n’侧是指向缓冲区的指针。 完成此操作后,整个问题就会消失,并且无论主机是哪个端点,例程都是准确的。 例如(没有尝试优化):

 inline void safe_htonl(unsigned char *netside, unsigned long value) { netside[3] = value & 0xFF; netside[2] = (value >> 8) & 0xFF; netside[1] = (value >> 16) & 0xFF; netside[0] = (value >> 24) & 0xFF; }; 

将指针转换为unsigned,然后返回指针。

unsigned int received_size = ntohl(*((unsigned *)((unsigned)dcc-> incoming_buf)));