The compiler produces this warning when I'm working with some code which looks like -
....
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);
}
....
}
where p = res
are of type struct addrinfo
and the types producing warnings are sockaddr_in
and sockaddr_in6
. The warning comes from statements :
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
All I want to know is what is causing this warning and what can I do to correct it if this is not the proper way to do things. Could I use any of static_cast
/ dynamic_cast
/ reinterpret_cast
here?
The exact warning is - cast from 'struct sockaddr *' to 'struct sockaddr_in *' increases required alignment from 2 to 4
.
TLDR: This warning doesn't indicate an error in your code, but you can avoid it by using a poper c++ reinterpret_cast
(thanks to @Kurt Stutsman).
Explanation:
Reason for the warning:
sockaddr
consists of a unsigned short (usually 16 bit) and a char array, so its alignment requirement is 2.
sockaddr_in
contains (among other things) a struct in_addr
which has an alignment requirement of 4 which in turn means sockaddr_in
also must be aligned to a 4 Byte boundary.
For that reason, casting an arbitrary sockaddr*
to an sockaddr_in*
changes the alignment requirement, and accessing the object via the new pointer would even violate aliasing rules and result in undefined behavior.
Why you can ignore it:
In your case, the object, p->ai_addr
is pointing to, most likely is a sockaddr_in
or sockaddr_in6
object anyway (as determined by checking ai_family
) and so the operation is safe. However you compiler doesn't know that and produces a warning.
It is essentially the same thing as using a static_cast
to cast a pointer to a base class to a pointer to a derived class - it is unsafe in the general case, but if you know the correct dynamic type extrinsically, it is well defined.
Solution:
I don't know a clean way around this (other than suppress the warning), which is not unusual with warnings enabled by -Weverything
. You could copy the object pointed to by p->ai_addr
byte by byte to an object of the appropriate type, but then you could (most likely) no longer use addr
the same way as before, as it would now point to a different (e.g. local) variable.
-Weverything
isn't something I would use for my usual builds anyway, because it adds far too much noise, but if you want to keep it, @Kurt Stutsman mentioned a good solution in the comments:
clang++ (g++ doesn't emit a warning in any case) doesn't emit a warning, if you use a reinterpret_cast
instead of the c style cast (which you shouldn't use anyway), although both have (in this case) exactly the same functionality. Maybe because reinterpret_cast
explicitly tells the compiler: "Trust me, I know, what I'm doing" .
On a side Note: In c++ code you don't need the struct
keywords.
Well -Weverything
enables quite a lot of warnings some of them are known to throw unwanted warnings.
Here your code fires the cast-align
warning, that says explicitely
cast from ... to ... increases required alignment from ... to ...
And it is the case here because the alignement for struct addr
is only 2 whereas it is 4 for struct addr_in
.
But you (and the programmer for getaddrinfo
...) know that the pointer p->ai_addr
already points to an actual struct addr_in
, so the cast is valid.
You can either:
- let the warning fire and ignore it - after all it is just a warning...
- silence it with
-Wno-cast-align
after -Weverything
I must admit that I seldom use -Weverything
for that reason, and only use -Wall
Alternatively, if you know that you only use CLang, you can use pragmas to explicetely turn the warning only on those lines:
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
....
}
To elaborate on the memcpy version. I thnk this is needed for ARM which cannot have misalligned data.
I created a struct that contains just the first two fields (I only needed port)
struct sockaddr_in_header {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
};
Then to get the port out, I used memcpy to move the data to the stack
struct sockaddr_in_header sinh;
unsigned short sin_port;
memcpy(&sinh, conn->local_sockaddr, sizeof(struct sockaddr_in_header));
And return the port
sin_port = ntohs(sinh.sin_port);
This answer is really related to getting the port on Arm
How do I cast sockaddr pointer to sockaddr_in on Arm
The powers that be think that to be the same question as this one, however I dont want to ignore warnings. Experience has taught me that is a bad idea.