Bla*_*ank 24 c strict-aliasing type-punning
我正在尝试使用GCC编译特定程序时修复两个警告.警告是:
警告:解除引用类型惩罚指针将破坏严格别名规则[-Wstrict-aliasing]
而这两个罪魁祸首是:
unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));
Run Code Online (Sandbox Code Playgroud)
和
*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
Run Code Online (Sandbox Code Playgroud)
incoming_buf和outgoing_buf定义如下:
char incoming_buf[LIBIRC_DCC_BUFFER_SIZE];
char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];
Run Code Online (Sandbox Code Playgroud)
这似乎与我一直在研究的那个警告的其他例子略有不同.我宁愿修复问题而不是禁用严格别名检查.
有很多建议要使用工会 - 这个案例可能是一个合适的工会?
oua*_*uah 40
首先,让我们来看看你为什么会得到别名违规警告.
别名规则简单地说,你只能通过它自己的类型,它的符号/无符号变量类型访问对象,或通过字符类型(char,signed char,unsigned char).
C表示违反别名规则会调用未定义的行为(所以不要!).
在你的程序的这一行:
unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));
Run Code Online (Sandbox Code Playgroud)
虽然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);
Run Code Online (Sandbox Code Playgroud)
您可以访问through 的char元素,因此它是一个锯齿违规.outgoing_bufunsigned int
提出的解决方案
要解决您的问题,您可以尝试直接在要访问的类型中定义数组的元素:
unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
Run Code Online (Sandbox Code Playgroud)
(顺便说一下,unsigned int实现的宽度是定义的,所以你应该考虑使用,uint32_t如果你的程序假设unsigned int是32位).
这样,您可以unsigned int通过类型访问元素,在不违反别名规则的情况下将对象存储在数组中char,如下所示:
*((char *) outgoing_buf) = expr_of_type_char;
Run Code Online (Sandbox Code Playgroud)
要么
char_lvalue = *((char *) incoming_buf);
Run Code Online (Sandbox Code Playgroud)
编辑:
我完全重写了我的答案,特别是我解释了为什么程序从编译器获得别名警告.
Ker*_* SB 22
要解决这个问题,请不要使用双关语和别名!读取类型的唯一"正确"方法T是分配类型T并在需要时填充其表示:
uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);
Run Code Online (Sandbox Code Playgroud)
简而言之:如果你想要一个整数,你需要做一个整数.没有办法以语言宽容的方式欺骗它.
允许的唯一指针转换(通常用于I/O)是将类型的现有变量的地址T视为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));
Run Code Online (Sandbox Code Playgroud)
简化说明 1. c++ 标准规定您应该尝试自己对齐数据,g++ 会加倍努力生成有关该主题的警告。2. 只有在您完全理解架构/系统和代码内部的数据对齐时才应该尝试它(例如,上面的代码在 Intel 32/64 上是肯定的;对齐 1;Win/Linux/Bsd/Mac) 3.使用上面代码的唯一实际原因是避免编译器警告,WHEN和IF你知道你在做什么