当我处理一些看起来像这样的代码时,编译器会产生这个警告 -
....
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
类型,产生警告的类型是 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
吗?
确切的警告是 - 从“struct sockaddr *”到“struct sockaddr_in *”的转换将所需的对齐方式从 2 增加到 4
。
最佳答案
TLDR:此警告并不表示您的代码中存在错误,但您可以通过使用 poper c++ reinterpret_cast
(感谢@Kurt Stutsman)来避免它。
解释:
警告原因:
sockaddr
由一个unsigned short(通常是16位)和一个char数组组成,所以它的对齐要求是2。sockaddr_in
包含(除其他事项外)一个struct in_addr
,它具有 4 的对齐要求,这反过来意味着sockaddr_in
也必须对齐到 4 字节边界。
因此,将任意 sockaddr*
转换为 sockaddr_in*
会改变对齐要求,并且通过新指针访问对象甚至会违反别名规则和结果在未定义的行为中。
为什么你可以忽略它:
在您的例子中,p->ai_addr
指向的对象很可能是 sockaddr_in
或 sockaddr_in6
对象(如通过检查 ai_family
) 确定,因此操作是安全的。但是,您的编译器并不知道这一点并会产生警告。
这与使用 static_cast
将指向基类的指针转换为指向派生类的指针本质上是一样的——在一般情况下这是不安全的,但是如果您知道正确的外在的动态类型,它是明确定义的。
解决方案:
我不知道解决这个问题的干净方法(除了抑制警告),这对于 -Weverything
启用的警告并不罕见。您可以将 p->ai_addr
指向的对象逐字节复制到适当类型的对象,但是您可能(很可能)不再使用 addr
和以前一样,因为它现在指向一个不同的(例如本地)变量。
-Weverything
无论如何我都不会在我的日常构建中使用它,因为它会增加太多噪音,但如果你想保留它,@Kurt Stutsman 在评论中提到了一个很好的解决方案:
clang++(g++ 在任何情况下都不会发出警告)不会发出警告,如果您使用 reinterpret_cast
而不是 c 样式转换(您无论如何都不应该使用) ,尽管两者(在本例中)具有完全相同的功能。可能是因为 reinterpret_cast
明确告诉编译器:“相信我,我知道我在做什么”。
旁注:在 C++ 代码中,您不需要 struct
关键字。
关于c++ - 从 sockaddr * 转换为 sockaddr_in * 增加了所需的对齐方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47706036/