Pregunta

Estoy tratando de arreglar dos advertencias al compilar un programa específico que usa GCC. Las advertencias son:

ADVERTENCIA: El puntero de TyeFerencing-Punned Reglas de alias de alias estricto [-wstrict-Aliasing

y los dos culpables son:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

y

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);

incoming_buf y saliente_buf se definen de la siguiente manera:

char                    incoming_buf[LIBIRC_DCC_BUFFER_SIZE];

char                    outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];

Esto parece sutilmente diferente a los otros ejemplos de esa advertencia que he estado examinando. Preferiría solucionar el problema en lugar de deshabilitar los controles de alias estrictos.

Ha habido muchas sugerencias para usar un sindicato, ¿cuál podría ser una unión adecuada para este caso?

¿Fue útil?

Solución

En primer lugar, examinemos por qué obtienes las advertencias de violación de alias.

Reglas de alias Simplemente diga que solo puede acceder a un objeto a través de su propio tipo, su tipo de variante firmada / sin firmar o a través de un tipo de caracteres (char, signed char, unsigned char).

C dice que violar las reglas de alias invoca un comportamiento indefinido (¡Así que no lo hagas!).

En esta línea de su programa:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

Aunque los elementos del incoming_buf la matriz es de tipo char, estás accediendo a ellos como unsigned int. De hecho, el resultado del operador de desertores en la expresión *((unsigned int*)dcc->incoming_buf) es de unsigned int escribe.

Esta es una violación de las reglas de alias, porque solo tiene derecho a acceder a elementos de incoming_buf Matriz a través (¡Ver resumen de reglas arriba!) char, signed char o unsigned char.

Observe que tiene exactamente el mismo problema de alias en su segundo culpable:

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);

Accedes a la char elementos de outgoing_buf mediante unsigned int, entonces es una violación de alias.

Solución propuesta

Para solucionar su problema, puede intentar tener los elementos de sus matrices directamente definidos en el tipo que desea acceder:

unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];

(Por cierto el ancho de unsigned int se define la implementación, por lo que debe considerar usar uint32_t Si su programa asume unsigned int es de 32 bits).

De esta manera podrías almacenar unsigned int objetos en su matriz sin violar las reglas de alias accediendo al elemento a través del tipo char, como esto:

*((char *) outgoing_buf) =  expr_of_type_char;

o

char_lvalue = *((char *) incoming_buf);

EDITAR:

He reelaborado por completo mi respuesta, en particular, explico por qué el programa recibe las advertencias de alias del compilador.

Otros consejos

Para solucionar el problema, No te pones en juego y alias! La única forma "correcta" de leer un tipo T es asignar un tipo T y pueblan su representación si es necesario:

uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);

En resumen: si desea un entero, debe hacer un entero. No hay forma de engañar a eso de una manera acondicionada por el idioma.

La única conversión de puntero que se le permite (para fines de E/S, generalmente) es tratar la dirección de una variable existente de tipo T como un char*, o más bien, como el puntero al primer elemento de una variedad de caracteres de tamaño sizeof(T).

union
{
    const unsigned int * int_val_p;
    const char* buf;
} xyz;

xyz.buf = dcc->incoming_buf;
unsigned int received_size = ntohl(*(xyz.int_val_p));

Explicación simplificada 1. C ++ Estado estándar que debe intentar alinear los datos usted mismo, G ++ hace un esfuerzo adicional para generar advertencias sobre el tema. 2. Solo debe intentarlo si comprende completamente la alineación de datos en su arquitectura/sistema y dentro de su código (por ejemplo, el código anterior es algo seguro en Intel 32/64; Alineación 1; Win/Linux/BSD/Mac) 3. La única razón práctica para usar el código anterior es evitar las advertencias del compilador, cuándo y si sabe lo que está haciendo

Si puedo, en mi humilde opinión, para este caso, el problema es el diseño de NTOHL y HTONL y API de funciones relacionadas. No deberían haber sido escritos como argumento numérico con retorno numérico. (Y sí, entiendo el punto de optimización de la macro) Deberían haber sido diseñados como el lado 'n' es un puntero a un búfer. Cuando esto se hace, todo el problema desaparece y la rutina es precisa, sea cual sea el endian que sea el anfitrión. Por ejemplo (sin intento de optimizar):

inline void safe_htonl(unsigned char *netside, unsigned long value) {
    netside[3] = value & 0xFF;
    netside[2] = (value >> 8) & 0xFF;
    netside[1] = (value >> 16) & 0xFF;
    netside[0] = (value >> 24) & 0xFF;
};

Si tiene razones que no le permiten cambiar el tipo de objeto fuente (como lo fue en mi caso), y está absolutamente seguro de que el código es correcto y hace lo que tiene la intención de ver con esa matriz de char, para evitar advertencias. puede hacer lo siguiente:

unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);

Lanza el puntero a Unsigned y luego de regreso al puntero.

unsigned int recibido_size = ntohl ( *((unsigned *) ((unsigned) dcc-> incoming_buf)));

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top