简体   繁体   English

从sockaddr *强制转换为sockaddr_in *增加所需的对齐方式

[英]cast from sockaddr * to sockaddr_in * increases required alignment

The compiler produces this warning when I'm working with some code which looks like - 当我使用某些看起来像这样的代码时,编译器会产生此警告:

....

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);
    }
....
}

where p = res are of type struct addrinfo and the types producing warnings are sockaddr_in and sockaddr_in6 . 其中p = res的类型为struct addrinfo ,产生警告的类型为sockaddr_insockaddr_in6 The warning comes from statements : 警告来自以下语句:

  • struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
  • struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;

All I want to know is what is causing this warning and what can I do to correct it if this is not the proper way to do things. 我只想知道是什么导致此警告,如果这不是正确的处理方法, 我该怎么办才能纠正它。 Could I use any of static_cast / dynamic_cast / reinterpret_cast here? 我可以在这里使用任何static_cast / dynamic_cast / reinterpret_cast吗?

The exact warning is - cast from 'struct sockaddr *' to 'struct sockaddr_in *' increases required alignment from 2 to 4 . 确切的警告是 - cast from 'struct sockaddr *' to 'struct sockaddr_in *' increases required alignment from 2 to 4

TLDR: This warning doesn't indicate an error in your code, but you can avoid it by using a poper c++ reinterpret_cast (thanks to @Kurt Stutsman). TLDR:此警告并不表示您的代码中有错误,但是您可以使用poper c ++ reinterpret_cast (由于@Kurt Stutsman)来避免此错误。


Explanation: 说明:

Reason for the warning : 警告的原因

  • sockaddr consists of a unsigned short (usually 16 bit) and a char array, so its alignment requirement is 2. sockaddr由一个无符号的short(通常为16位)和一个char数组组成,因此其对齐要求为2。
  • sockaddr_in contains (among other things) a struct in_addr which has an alignment requirement of 4 which in turn means sockaddr_in also must be aligned to a 4 Byte boundary. sockaddr_in包含(除其他外) struct in_addr ,其对齐要求为4,这又意味着sockaddr_in也必须对齐4字节边界。

For that reason, casting an arbitrary sockaddr* to an sockaddr_in* changes the alignment requirement, and accessing the object via the new pointer would even violate aliasing rules and result in undefined behavior. 因此,将任意sockaddr*强制转换为sockaddr_in*更改对齐要求,并且通过新指针访问对象甚至会违反别名规则,并导致未定义的行为。

Why you can ignore it : 为什么您可以忽略它

In your case, the object, p->ai_addr is pointing to, most likely is a sockaddr_in or sockaddr_in6 object anyway (as determined by checking ai_family ) and so the operation is safe. 在您的情况下,对象p->ai_addr指向, p->ai_addr都很可能是sockaddr_insockaddr_in6对象(通过检查ai_family确定),因此该操作是安全的。 However you compiler doesn't know that and produces a warning. 但是,您的编译器不知道并生成警告。

It is essentially the same thing as using a static_cast to cast a pointer to a base class to a pointer to a derived class - it is unsafe in the general case, but if you know the correct dynamic type extrinsically, it is well defined. 这与使用static_cast将指向基类的指针static_cast转换为派生类的指针本质上是相同的-在通常情况下这是不安全的,但是如果您在外部了解正确的动态类型,则定义明确。

Solution: 解:
I don't know a clean way around this (other than suppress the warning), which is not unusual with warnings enabled by -Weverything . 我不知道有什么解决方法(除了抑制警告),这与-Weverything启用的警告并不罕见。 You could copy the object pointed to by p->ai_addr byte by byte to an object of the appropriate type, but then you could (most likely) no longer use addr the same way as before, as it would now point to a different (eg local) variable. 您可以将p->ai_addr指向的对象一个字节一个字节地复制到适当类型的对象,但随后(很有可能)不再像以前那样使用addr了,因为它现在指向不同的(例如局部变量。
-Weverything isn't something I would use for my usual builds anyway, because it adds far too much noise, but if you want to keep it, @Kurt Stutsman mentioned a good solution in the comments: -Weverything无论如何,我无论如何都不会使用任何东西,因为它会增加太多噪音,但是如果您想保留它,@ Kurt Stutsman在评论中提到了一个很好的解决方案:

clang++ (g++ doesn't emit a warning in any case) doesn't emit a warning, if you use a reinterpret_cast instead of the c style cast (which you shouldn't use anyway), although both have (in this case) exactly the same functionality. 如果您使用reinterpret_cast而不是c样式强制转换(无论如何都不应使用),则clang ++(g ++在任何情况下均不会发出警告)不会发出警告,尽管两者都(在这种情况下)完全相同相同的功能。 Maybe because reinterpret_cast explicitly tells the compiler: "Trust me, I know, what I'm doing" . 也许是因为reinterpret_cast明确告诉编译器: “相信我,我知道我在做什么”


On a side Note: In c++ code you don't need the struct keywords. 附带说明:在c ++代码中,您不需要struct关键字。

Well -Weverything enables quite a lot of warnings some of them are known to throw unwanted warnings. 很好- -Weverything启用很多警告,其中一些警告会引发不必要的警告。

Here your code fires the cast-align warning, that says explicitely 在这里,您的代码会触发cast-align警告,即明确表示

cast from ... to ... increases required alignment from ... to ... 从...转换为...将所需的对齐方式从...转换为...

And it is the case here because the alignement for struct addr is only 2 whereas it is 4 for struct addr_in . 这是因为struct addr struct addr_in方式只有2而struct addr_in是4。

But you (and the programmer for getaddrinfo ...) know that the pointer p->ai_addr already points to an actual struct addr_in , so the cast is valid. 但是 (以及getaddrinfo的程序员)知道指针p->ai_addr已经指向实际的struct addr_in ,因此struct addr_in有效。

You can either: 您可以:

  • let the warning fire and ignore it - after all it is just a warning... 让警告发出并忽略它-毕竟这只是警告...
  • silence it with -Wno-cast-align after -Weverything 使用-Wno-cast-align后将其-Wno-cast-align -Weverything

I must admit that I seldom use -Weverything for that reason, and only use -Wall 我必须承认,出于这个原因,我很少使用-Weverything ,而仅使用-Wall


Alternatively, if you know that you only use CLang, you can use pragmas to explicetely turn the warning only on those lines : 另外,如果您知道仅使用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

....
}

To elaborate on the memcpy version. 详细介绍一下memcpy版本。 I thnk this is needed for ARM which cannot have misalligned data. 我想这对于不会产生错误数据的ARM是必需的。

I created a struct that contains just the first two fields (I only needed port) 我创建了仅包含前两个字段的结构(我只需要端口)

struct sockaddr_in_header {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
};

Then to get the port out, I used memcpy to move the data to the stack 然后要移出端口,我使用了memcpy将数据移到堆栈中

struct sockaddr_in_header   sinh;
unsigned short              sin_port;

memcpy(&sinh, conn->local_sockaddr, sizeof(struct sockaddr_in_header));

And return the port 并返回端口

sin_port = ntohs(sinh.sin_port);

This answer is really related to getting the port on Arm 这个答案确实与获得Arm的端口有关

How do I cast sockaddr pointer to sockaddr_in on Arm 如何在Arm上将sockaddr指针转换为sockaddr_in

The powers that be think that to be the same question as this one, however I dont want to ignore warnings. 认为与这个问题相同的权力,但是我不想忽略警告。 Experience has taught me that is a bad idea. 经验告诉我,这是一个坏主意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM