修复取消引用类型惩罚指针将破坏严格别名
我正在尝试使用GCC编译特定程序时修复两个警告。 警告是:
警告:解除引用类型惩罚指针将破坏严格别名规则[-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];
这似乎与我一直在研究的那个警告的其他例子略有不同。 我宁愿修复问题而不是禁用严格别名检查。
有很多建议要使用联合 – 这个案例可能是一个合适的联合体?
首先,让我们来看看你为什么会得到别名违规警告。
别名规则只是说您只能通过自己的类型,签名/无符号变体类型或字符类型( char
, signed char
, unsigned 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
数组的元素(参见上面的规则摘要!) char
, signed char
或unsigned char
。
请注意,您在第二个罪魁祸首中存在完全相同的别名问题:
*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
您可以通过unsigned int
访问outgoing_buf
的char
元素,因此它是一个别名冲突。
提出的解决方案
要解决您的问题,您可以尝试直接在要访问的类型中定义数组的元素:
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)));