从sockaddr*转换为sockaddr_in*增加了所需的对齐

Abh*_*yal 5 c++ linux networking casting clang

当我使用一些看起来像的代码时,编译器会产生此警告.

....

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);
    }
....
}
Run Code Online (Sandbox Code Playgroud)

其中p = res是式的struct addrinfo,并产生警告类型是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.

Mik*_*eMB 7

TLDR:此警告并不表示代码中存在错误,但您可以通过使用poper c ++来避免它reinterpret_cast(感谢@Kurt Stutsman).


说明:

警告原因:

  • sockaddr 由无符号短(通常为16位)和char数组组成,因此其对齐要求为2.
  • sockaddr_in包含(其中包括)struct in_addr具有4的对齐要求的a ,其又意味着sockaddr_in还必须与4字节边界对齐.

出于这个原因,强制转换sockaddr*sockaddr_in* 更改对齐要求,并通过新指针访问对象甚至会违反别名规则并导致未定义的行为.

为什么你可以忽略它:

在您的情况下,对象p->ai_addr指向,最有可能是一个sockaddr_insockaddr_in6对象(通过检查确定ai_family),因此操作是安全的.但是,编译器不知道并生成警告.

它与使用a static_cast将指向基类的指针强制转换为指向派生类的指针基本相同- 在一般情况下它是不安全的,但如果你知道外部的正确动态类型,它就是很好定义的.

解决方案:
我不知道一个干净的方法(除了抑制警告),这在启用警告的情况下并不罕见-Weverything.您可以将逐p->ai_addr字节指向的对象复制到相应类型的对象,但随后您可能(很可能)不再使用addr与之前相同的方式,因为它现在指向不同的(例如本地)变量.
-Weverything不管怎样我不会用于我常用的版本,因为它会增加太多噪音,但如果你想保留它,@ Kurt Stutsman在评论中提到了一个很好的解决方案:

clang ++(g ++在任何情况下都不会发出警告)不发出警告,如果你使用的reinterpret_cast不是c样式演员(你不应该使用它),尽管两者都(在这种情况下)完全是相同的功能.也许是因为reinterpret_cast明确告诉编译器:"相信我,我知道,我在做什么".


另一方面注意:在c ++代码中,您不需要struct关键字.


Ser*_*sta 6

很好地-Weverything发出了很多警告,其中一些警告会发出不必要的警告.

在这里,您的代码会触发cast-align警告,明确说明

从...转换为......增加从......到...的所需对齐

这就是这种情况,因为对齐struct addr只有2,而它是4 struct addr_in.

但是(以及getaddrinfo...... 的程序员)知道指针p->ai_addr已经指向实际struct addr_in,所以转换是有效的.

你可以:

  • 让警告开火并忽略它 - 毕竟它只是一个警告......
  • 与它沉默-Wno-cast-align-Weverything

我必须承认,我很少-Weverything因为这个原因而使用,而且只能使用-Wall


或者,如果您知道仅使用CLang,则可以使用编译指示仅在这些行上显示警告:

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

....
}
Run Code Online (Sandbox Code Playgroud)