El compilador produce esta advertencia cuando estoy trabajando con un código que se parece a:
.... 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); } .... } donde p = res son de tipo struct addrinfo y los tipos que generan advertencias son sockaddr_in y sockaddr_in6 . La advertencia proviene de declaraciones:
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; Todo lo que quiero saber es qué está causando esta advertencia y qué puedo hacer para corregirla si esta no es la forma correcta de hacer las cosas. ¿Podría usar aquí static_cast / dynamic_cast / reinterpret_cast ?
La advertencia exacta es - cast from 'struct sockaddr *' to 'struct sockaddr_in *' increases required alignment from 2 to 4 .
TLDR: esta advertencia no indica un error en su código, pero puede evitarlo usando poper c ++ reinterpret_cast (gracias a @Kurt Stutsman).
Explicación:
Motivo de la advertencia :
sockaddr consta de un short sin firmar (generalmente de 16 bits) y una matriz de caracteres, por lo que su requisito de alineación es 2.sockaddr_in contiene (entre otras cosas) una struct in_addr que tiene un requisito de alineación de 4, lo que a su vez significa que sockaddr_in también debe alinearse con un límite de 4 bytes. Por esa razón, convertir un sockaddr* arbitrario en un sockaddr_in* cambia el requisito de alineación, y acceder al objeto a través del nuevo puntero incluso violaría las reglas de creación de alias y daría como resultado un comportamiento indefinido.
Por qué puedes ignorarlo :
En su caso, el objeto al que apunta p->ai_addr , lo más probable es que sea un objeto sockaddr_in o sockaddr_in6 de todos modos (según lo determinado al verificar ai_family ) y, por lo tanto, la operación es segura. Sin embargo, su compilador no lo sabe y produce una advertencia.
Es esencialmente lo mismo que usar un static_cast para convertir un puntero a una clase base en un puntero a una clase derivada: no es seguro en el caso general, pero si conoce el tipo dinámico correcto extrínsecamente, está bien definido.
Solución:
No conozco una forma limpia de evitar esto (aparte de suprimir la advertencia), lo cual no es inusual con las advertencias habilitadas por -Weverything . Podría copiar el objeto al que apunta p->ai_addr byte a byte en un objeto del tipo apropiado, pero entonces (lo más probable) ya no podría usar addr de la misma manera que antes, ya que ahora apuntaría a un ( ej., local) variable.
-Weverything no es algo que usaría para mis compilaciones habituales de todos modos, porque agrega demasiado ruido, pero si desea mantenerlo, @Kurt Stutsman mencionó una buena solución en los comentarios:
clang ++ (g ++ no emite una advertencia en ningún caso) no emite una advertencia, si usa un reinterpret_cast en lugar del molde de estilo c (que no debería usar de todos modos), aunque ambos tienen (en este caso) exactamente la misma funcionalidad. Tal vez porque reinterpret_cast le dice explícitamente al compilador: "Confía en mí, sé lo que estoy haciendo" .
Por un lado Nota: en el código c ++ no necesita las palabras clave de struct .
Bueno, -Weverything permite una gran cantidad de advertencias, algunas de ellas son conocidas por lanzar advertencias no deseadas.
Aquí su código activa la advertencia cast-align , que dice explícitamente
lanzar de... a... aumenta la alineación requerida de... a...
Y es el caso aquí porque la alineación para struct addr es solo 2 mientras que es 4 para struct addr_in .
Pero usted (y el programador de getaddrinfo ...) saben que el puntero p->ai_addr ya apunta a una estructura real struct addr_in , por lo que la conversión es válida.
Tu también puedes:
-Wno-cast-align después de -Weverything Debo admitir que rara vez uso -Weverything por esa razón, y solo uso -Wall
Alternativamente, si sabe que solo usa CLang, puede usar pragmas para activar explícitamente la advertencia solo en esas líneas :
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 .... }Para profundizar en la versión memcpy. Creo que esto es necesario para ARM que no puede tener datos desalineados.
Creé una estructura que contiene solo los dos primeros campos (solo necesitaba el puerto)
struct sockaddr_in_header { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ };Luego, para sacar el puerto, usé memcpy para mover los datos a la pila.
struct sockaddr_in_header sinh; unsigned short sin_port; memcpy(&sinh, conn->local_sockaddr, sizeof(struct sockaddr_in_header));Y devolver el puerto
sin_port = ntohs(sinh.sin_port);Esta respuesta está realmente relacionada con obtener el puerto en Arm
¿Cómo lanzo el puntero sockaddr a sockaddr_in en Arm?
Los poderes fácticos piensan que es la misma pregunta que esta, sin embargo, no quiero ignorar las advertencias. La experiencia me ha enseñado que es una mala idea.