可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm using Beej's Guide to Networking and came across an aliasing issue. He proposes a function to return either the IPv4 or IPv6 address of a particular struct:
1 void *get_in_addr( struct sockaddr *sa )
2 {
3 if (sa->sa_family == AF_INET)
4 return &(((struct sockaddr_in*)sa)->sin_addr);
5 else
6 return &(((struct sockaddr_in6*)sa)->sin6_addr);
7 }
This causes GCC to spit out a strict-aliasing error for sa on line 3. As I understand it, it is because I call this function like so:
struct sockaddr_storage their_addr;
...
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
connection_name,
sizeof connection_name);
I'm guessing the aliasing has to do with the fact that the their_addr
variable is of type sockaddr_storage
and another pointer of a differing type points to the same memory.
Is the best way to get around this sticking sockaddr_storage
, sockaddr_in
, and sockaddr_in6
into a union? It seems like this should be well worn territory in networking, I just can't find any good examples with best practices.
Also, if anyone can explain exactly where the aliasing issue takes place, I'd much appreciate it.
回答1:
I tend to do this to get GCC do the right thing with type-punning, which is explicitly allowed with unions:
/*! Multi-family socket end-point address. */
typedef union address
{
struct sockaddr sa;
struct sockaddr_in sa_in;
struct sockaddr_in6 sa_in6;
struct sockaddr_storage sa_stor;
}
address_t;
回答2:
I tend to do this to get GCC do the right thing with type-punning, which is explicitly allowed with unions
I am pretty sure this (mis)use of union will not work (or only by accident) with GCC:
short type_pun2 (int i, int *pi, short *ps) {
*pi = i;
return *ps;
}
union U {
int i;
short s;
};
short type_pun (int i) {
U u;
return type_pun2 (i, &u.i, &u.s);
}
The correct way to do that is with memcpy
, not union
.
回答3:
I recently had a similar alias warning on HPUX system when trying to write code to get the MAC address of the machine
The &(((struct sockaddr_in *)addr)->sin_addr)
complains about strict-aliasing rules
This is the code in some context
char ip[INET6_ADDRSTRLEN] = {0};
strucut sockaddr *addr
...
get addr from ioctl(socket,SOCGIFCONF...) call
...
inet_ntop(AF_INET, &(((struct sockaddr_in *)addr)->sin_addr),ip,sizeof ip);
I overcame the aliasing warning by doing the following
struct sockaddr_in sin;
memcpy(&sin,addr,sizeof(struct sockaddr));
inet_ntop(AF_INET, &sin.sin_addr,ip,sizeof ip);
And whilst this is potentially dangerous I added the following lines before it
static_assert(sizeof(sockaddr)==sizeof(sockaddr_in));
I'm not sure if that is something would be considered bad practice, but it worked and was cross platform to other *Nix flavors and compilers
回答4:
The issue has nothing to do with the call to the function. Rather, it's with ((struct sockaddr_in*)sa)->sin_addr
. The problem is that sa
is a pointer of one type, but you're casting it to a pointer of a different type and then dereferencing it. This breaks a rule called "strict aliasing", which says that variables of different types can never alias. In your case, aliasing to a different type is exactly what you want to do.
The simple solution is to turn off this optimization, which allows aliasing in this manner. On GCC, the flag is -fno-strict-aliasing
.
The better solution is to use a union, as mentioned by Nikolai.
void *get_in_addr(struct sockaddr *sa)
{
union {
struct sockaddr *sa;
struct sockaddr_in *sa_in;
struct sockaddr_in6 *sa_in6;
} u;
u.sa = sa;
if (sa->sa_family == AF_INET)
return &(u.sa_in->sin_addr);
else
return &(u.sa_in6->sin6_addr);
}
That said, I can't actually get GCC to give me a warning when using your original code, so I'm not sure if this buys you anything.