我试图在使用GCC编译特定程序时修复两个警告。 警告是:
警告:取消引用类型双关语指针将断开严格别名规则 [-Wstrict-别名]
而罪魁祸首是:
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
的宽度是实现定义的,因此如果您的程序假定unsigned int
是 32 位,则应考虑使用 uint32_t
(。
这样,您可以通过类型 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*
,或者更确切地说,作为指向大小为 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. 只有当您完全了解架构/系统和代码内部的数据对齐时,您才应该尝试这样做(例如上面的代码在英特尔 32/64 上是确定的事情; 对齐 1;Win/Linux/Bsd/Mac(3.使用上述代码的唯一实际原因是避免编译器警告,WHEN和IF 你知道你在做什么
恕我直言,在这种情况下,问题在于 ntohl 和 htonl 以及相关函数 API 的设计。它们不应该写成带有数字返回的数字参数。(是的,我了解宏观优化点(它们应该被设计为"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;
};
如果您有不允许更改源对象类型的原因(就像我的情况一样(,并且您绝对确信代码是正确的,并且它执行了打算对该 char 数组执行的操作,为了避免警告,您可以执行以下操作:
unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);
我最近将一个项目从 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 升级中被捕获(,我会继续使用指针类型的联合,如 Real Name 在此线程上描述的那样。
C cast不起作用,但reinterpret_cast<>类似情况下帮助了我。
如果您确定自己知道自己在做什么,请执行以下操作:
void *tmp = dcc->incoming_buf;
unsigned int received_size = ntohl (*((unsigned int*) tmp));
或只是:
unsigned int received_size = ntohl (*((unsigned int*) ((void *) dcc->incoming_buf)));
将指针强制转换为无符号,然后返回指针。
unsigned int received_size = ntohl (*((unsigned *(((unsigned( dcc->incoming_buf(( (;