我正在尝试使用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];
这似乎与我一直在研究的那个警告的其他例子略有不同。我宁愿修复问题而不是禁用严格别名检查。
有许多使用工会的建议 - 对于这种情况,什么可能是合适的工会?
答案 0 :(得分:39)
首先,让我们来看看为什么会收到别名违规警告。
别名规则只是说您只能通过自己的类型,签名/无符号变体类型或字符类型(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
或{{ 1}}。
请注意,您在第二个罪魁祸首中存在完全相同的别名问题:
unsigned char
您通过*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
访问char
的{{1}}元素,因此存在别名冲突。
建议的解决方案
要解决您的问题,您可以尝试直接在要访问的类型中定义数组的元素:
outgoing_buf
(顺便说一下unsigned int
的宽度是实现定义的,所以如果你的程序假定unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
是32位的话,你应该考虑使用unsigned int
。
这样,您可以通过类型uint32_t
访问元素,在不违反别名规则的情况下将unsigned int
个对象存储在数组中,如下所示:
unsigned int
或
char
修改强>
我完全重写了我的答案,特别是我解释了为什么程序从编译器获得别名警告。
答案 1 :(得分:20)
要解决此问题,请不要使用双关语!读取类型T
的唯一“正确”方法是分配类型T
并在需要时填充其表示形式:
uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);
简而言之:如果你想要一个整数,你需要做一个整数。没有办法以语言宽容的方式欺骗它。
允许的唯一指针转换(通常用于I / O)是将 T
类型的现有变量的地址视为{{1}或者更确切地说,作为指向大小为char*
的字符数组的第一个元素的指针。
答案 2 :(得分:4)
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你知道你在做什么
答案 3 :(得分:0)
如果我可以,恕我直言,对于这种情况,问题是ntohl和htonl及相关功能API的设计。它们不应该用数字返回写成数字参数。 (是的,我理解宏优化点) 他们应该被设计为' n' side是指向缓冲区的指针。完成此操作后,整个问题就会消失,并且无论主机是哪个端点,例程都是准确的。 例如(没有尝试优化):
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;
};
答案 4 :(得分:0)
如果您有不允许更改源对象类型的原因(例如在我的情况下),并且您完全有信心代码是正确的,并且可以使用该char数组进行操作,为避免警告,您可以执行以下操作:
unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);
答案 5 :(得分:0)
我最近将一个项目从GCC 6升级到了GCC 9,并开始看到此警告。该项目位于32位微控制器上,我创建了一个结构来访问32位机器寄存器的各个字节:
struct TCC_WEXCTRL_t
{
byte OTMX;
byte DTIEN;
byte DTLS;
byte DTHS;
};
然后编码:
((TCC_WEXCTRL_t *)&TCC0->WEXCTRL)->DTLS = PwmLoDeadTime;
会在新的编译器中产生警告。我发现我可以通过将结构体与原始类型结合在联合体中来消除警告:
union TCC_WEXCTRL_t
{
TCC_WEXCTRL_Type std;
struct
{
byte OTMX;
byte DTIEN;
byte DTLS;
byte DTHS;
};
};
其中TCC_WEXCTRL_Type
是制造商的头文件中提供的WEXCTRL
成员的类型。
我不确定这是否被视为完全符合要求的修复程序,或者GCC是否只是未能捕获到该修复程序。如果这没有用(或者被其他GCC升级所困扰),我将继续使用指针类型的并集,如实名对此线程所述。
答案 6 :(得分:0)
C转换不起作用,但是reinterpret_cast <>在类似情况下也帮助了我。
答案 7 :(得分:-2)
将指针转换为无符号,然后返回指针。
unsigned int received_size = ntohl(*((unsigned *)((unsigned)dcc-&gt; incoming_buf)));