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

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_bufoutgoing_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 charunsigned 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).


Rea*_*ame 6

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你知道你在做什么