编译器产生这个警告,当我正在处理一些代码,看起来像 –
.... for(p = res; p != NULL; p = p->ai_next) { void *addr; std::string ipVer = "IPv0"; if(p->ai_family == AF_INET) { ipVer = "IPv4"; struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; addr = &(ipv4->sin_addr); } else { ipVer = "IPv6"; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; addr = &(ipv6->sin6_addr); } .... }
其中p = res
是struct addrinfo
types,生成警告的types是sockaddr_in
和sockaddr_in6
。 警告来自陈述:
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
我只想知道是什么导致这个警告,如果这不是正确的做事方法, 我该怎么办才能纠正它。 我可以在这里使用static_cast
/ dynamic_cast
/ reinterpret_cast
任何一个吗?
确切的警告 – cast from 'struct sockaddr *' to 'struct sockaddr_in *' increases required alignment from 2 to 4
。
TLDR:这个警告并不代表你的代码中有错误,但你可以通过使用poper c ++ reinterpret_cast
来避免它(感谢@Kurt Stutsman)。
说明:
警告原因 :
sockaddr
由一个unsigned short(通常是16位)和一个char数组组成,所以它的对齐要求是2。 sockaddr_in
包含(除其他外)一个struct in_addr
,它的对齐要求为4,这意味着sockaddr_in
也必须与4 Byte边界对齐。 因此,将任意sockaddr*
转换为sockaddr_in*
改变对齐要求,并且通过新指针访问对象甚至会违反别名规则并导致不确定的行为。
为什么你可以忽略它 :
在你的情况下, p->ai_addr
指向的对象很可能是sockaddr_in
或sockaddr_in6
对象(通过检查ai_family
来确定),因此操作是安全的。 但是,你编译器不知道,并产生一个警告。
与使用static_cast
将指向基类的指针转换为指向派生类的指针基本上是一样的 – 在一般情况下它是不安全的,但是如果您知道外部正确的动态类型,就可以很好地定义它。
解:
我不知道一个干净的方法(除了压制警告),这是不是一般的警告 – -Weverything
启用。您可以将 p->ai_addr
指向的对象p->ai_addr
字节复制到适当类型的对象,但是您可能(很可能)不再像以前那样使用addr
,因为它现在指向一个不同的(例如本地)变量。
-Weverything
无论如何,我们不会使用任何东西,因为它增加了太多的噪音,但如果你想保留它,@Kurt Stutsman在评论中提到了一个很好的解决方案:
铿锵声++(g ++在任何情况下都不会发出警告)不会发出警告,如果你使用reinterpret_cast
而不是c风格的转换(你不应该使用它),尽管两者都有(在这种情况下)相同的功能。 也许是因为reinterpret_cast
明确告诉编译器: “相信我,我知道我在做什么” 。
注意:在c ++代码中,你不需要struct
关键字。
好 – 我们所有的-Weverything
提供相当多的警告,其中一些警告会引起不必要的警告。
在这里,你的代码触发了cast-align
警告,明确地说
从…投掷…到… …增加所需的对齐从…到…
在这里是这样的,因为struct addr
struct addr_in
方式只有2,而struct addr_in
则是4。
但是你 (和getaddrinfo
的程序员)知道指针p->ai_addr
已经指向了一个实际的struct addr_in
,所以这个转换是有效的。
你可以:
-Wno-cast-align
after -Weverything
我必须承认,我很少使用-Weverything
出于这个原因,只使用“ -Wall
或者,如果您知道您只使用CLang,则可以使用pragmas来仅在这些行上转换警告:
for(p = res; p != NULL; p = p->ai_next) { void *addr; std::string ipVer = "IPv0"; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-align" if(p->ai_family == AF_INET) { ipVer = "IPv4"; struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; addr = &(ipv4->sin_addr); } else { ipVer = "IPv6"; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; addr = &(ipv6->sin6_addr); } #pragma clang diagnostic pop .... }