我正在尝试使用GCC编译特定程序时修复两个警告 . 警告是:
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
而这两个罪魁祸首是:
unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));
和
*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
incoming_buf 和 outgoing_buf 定义如下:
char incoming_buf[LIBIRC_DCC_BUFFER_SIZE];
char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];
这似乎与我一直在研究的那个警告的其他例子略有不同 . 我宁愿修复问题而不是禁用严格别名检查 .
有很多建议要使用联合 - 这个案例可能是一个合适的联合体?
5 回答
首先,让我们来看看你为什么会得到别名违规警告 .
Aliasing rules 只是说您只能通过自己的类型,签名/无符号变体类型或字符类型(
char
,signed char
,unsigned char
)访问对象 .C表示违反别名规则会调用未定义的行为(所以不要!) .
在你的程序的这一行:
虽然
incoming_buf
数组的元素类型为char
,但您将其作为unsigned int
进行访问 . 实际上,表达式*((unsigned int*)dcc->incoming_buf)
中的解除引用运算符的结果是unsigned int
类型 .这违反了别名规则,因为您只有权访问
incoming_buf
数组的元素(参见上面的规则摘要!)char
,signed char
或unsigned char
.请注意,您在第二个罪魁祸首中存在完全相同的别名问题:
您通过
unsigned int
访问outgoing_buf
的char
元素,因此它是一个锯齿违规 .Proposed solution
要解决您的问题,您可以尝试直接在要访问的类型中定义数组的元素:
(顺便说一下
unsigned int
的宽度是实现定义的,所以如果你的程序假设unsigned int
是32位,你应该考虑使用uint32_t
) .这样,您可以通过类型
char
访问元素,从而在不违反别名规则的情况下将unsigned int
对象存储在数组中,如下所示:要么
EDIT:
我完全重写了我的答案,特别是我解释了为什么程序从编译器获得别名警告 .
将指针转换为unsigned,然后返回指针 .
unsigned int received_size = ntohl(*((unsigned *)((unsigned)dcc-> incoming_buf)));
如果我可以,恕我直言,对于这种情况,问题是ntohl和htonl及相关功能API的设计 . 它们不应该用数字返回写成数字参数 . (是的,我理解宏优化点)它们应该被设计为'n'侧是指向缓冲区的指针 . 完成此操作后,整个问题就会消失,并且无论主机是哪个 endpoints ,例程都是准确的 . 例如(没有尝试优化):
简化说明1. c标准规定您应该尝试自己对齐数据,g需要额外的努力才能生成有关主题的警告 . 2.只有在完全理解架构/系统和代码内部的数据对齐时才应该尝试它(例如上面的代码在Intel 32/64上是肯定的;对齐1; Win / Linux / Bsd / Mac) 3.使用上述代码的唯一实际原因是避免编译器警告,WHEN和IF你知道你在做什么
要解决这个问题,请不要使用双关语和别名!读取类型
T
的唯一"correct"方法是分配类型T
并在需要时填充其表示:简而言之:如果你想要一个整数,你需要做一个整数 . 没有办法以语言宽容的方式欺骗它 .
允许的唯一指针转换(通常用于I / O)是将
T
类型的现有变量的地址视为char*
,或者更确切地说,作为指向字符数组的第一个元素的指针 . 尺寸sizeof(T)
.