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.
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:
The correct way to do that is with
memcpy
, notunion
.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 rulesThis is the code in some context
I overcame the aliasing warning by doing the following
And whilst this is potentially dangerous I added the following lines before it
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 thatsa
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.
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.