![](/img/trans.png)
[英]Is the type cast from sockaddr_in* to sockaddr* a violation of "strict aliasing rule"?
[英]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_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
。
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_in
或sockaddr_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.