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:
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
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.
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
.
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;