簡體   English   中英

從sockaddr *強制轉換為sockaddr_in *增加所需的對齊方式

[英]cast from sockaddr * to sockaddr_in * increases required 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 = 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

TLDR:此警告並不表示您的代碼中有錯誤,但是您可以使用poper c ++ reinterpret_cast (由於@Kurt Stutsman)來避免此錯誤。


說明:

警告的原因

  • sockaddr由一個無符號的short(通常為16位)和一個char數組組成,因此其對齊要求為2。
  • sockaddr_in包含(除其他外) struct in_addr ,其對齊要求為4,這又意味着sockaddr_in也必須對齊4字節邊界。

因此,將任意sockaddr*強制轉換為sockaddr_in*更改對齊要求,並且通過新指針訪問對象甚至會違反別名規則,並導致未定義的行為。

為什么您可以忽略它

在您的情況下,對象p->ai_addr指向, p->ai_addr都很可能是sockaddr_insockaddr_in6對象(通過檢查ai_family確定),因此該操作是安全的。 但是,您的編譯器不知道並生成警告。

這與使用static_cast將指向基類的指針static_cast轉換為派生類的指針本質上是相同的-在通常情況下這是不安全的,但是如果您在外部了解正確的動態類型,則定義明確。

解:
我不知道有什么解決方法(除了抑制警告),這與-Weverything啟用的警告並不罕見。 您可以將p->ai_addr指向的對象一個字節一個字節地復制到適當類型的對象,但隨后(很有可能)不再像以前那樣使用addr了,因為它現在指向不同的(例如局部變量。
-Weverything無論如何,我無論如何都不會使用任何東西,因為它會增加太多噪音,但是如果您想保留它,@ Kurt Stutsman在評論中提到了一個很好的解決方案:

如果您使用reinterpret_cast而不是c樣式強制轉換(無論如何都不應使用),則clang ++(g ++在任何情況下均不會發出警告)不會發出警告,盡管兩者都(在這種情況下)完全相同相同的功能。 也許是因為reinterpret_cast明確告訴編譯器: “相信我,我知道我在做什么”


附帶說明:在c ++代碼中,您不需要struct關鍵字。

很好- -Weverything啟用很多警告,其中一些警告會引發不必要的警告。

在這里,您的代碼會觸發cast-align警告,即明確表示

從...轉換為...將所需的對齊方式從...轉換為...

這是因為struct addr struct addr_in方式只有2而struct addr_in是4。

但是 (以及getaddrinfo的程序員)知道指針p->ai_addr已經指向實際的struct addr_in ,因此struct addr_in有效。

您可以:

  • 讓警告發出並忽略它-畢竟這只是警告...
  • 使用-Wno-cast-align后將其-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

....
}

詳細介紹一下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的端口有關

如何在Arm上將sockaddr指針轉換為sockaddr_in

認為與這個問題相同的權力,但是我不想忽略警告。 經驗告訴我,這是一個壞主意。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM