从sockaddr *投射到sockaddr_in *会增加所需的alignment

编译器产生这个警告,当我正在处理一些代码,看起来像 –

.... 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 = resstruct addrinfotypes,生成警告的types是sockaddr_insockaddr_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_insockaddr_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 .... }