当我使用一些看起来像 -
的代码时,编译器会发出此警告....
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
中的任何一个吗?
确切的警告是 - cast from 'struct sockaddr *' to 'struct sockaddr_in *' increases required alignment from 2 to 4
。
答案 0 :(得分:7)
TLDR:此警告并未表示代码中存在错误,但您可以使用poper c ++ reinterpret_cast
来避免错误(感谢@Kurt Stutsman)。< / p>
<强>解释强>
警告原因:
sockaddr
由无符号短(通常为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
明确告诉编译器:&#34;相信我,我知道,我在做什么&#34; 。
另一方面注意:在c ++代码中,您不需要struct
个关键字。
答案 1 :(得分:6)
好-Weverything
启用了很多警告,其中一些警告会发出不必要的警告。
此处您的代码会触发cast-align
警告,明确说明
从...转换为......从...增加所需的对齐...
这就是这种情况,因为struct addr
的对齐只有2,而struct addr_in
则为4。
但是你(以及getaddrinfo
的程序员......)知道指针p->ai_addr
已经指向实际的struct addr_in
,所以演员阵容是有效的。
你可以:
-Wno-cast-align
-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
....
}
答案 2 :(得分:1)
详细说明memcpy版本。我认为这是ARM所必需的,它不能有错误的数据。
我创建了一个只包含前两个字段的结构(我只需要端口)
struct sockaddr_in_header {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
};
然后为了获得端口,我使用memcpy将数据移动到堆栈
struct sockaddr_in_header sinh;
unsigned short sin_port;
memcpy(&sinh, conn->local_sockaddr, sizeof(struct sockaddr_in_header));
返回端口
sin_port = ntohs(sinh.sin_port);
这个答案与获取Arm上的端口非常相关
How do I cast sockaddr pointer to sockaddr_in on Arm
认为与这个问题是同一个问题的权力,但是我不想忽视警告。经验告诉我这是一个坏主意。