Correction du déréférencement du pointeur typé qui va rompre l’aliasing ssortingct

J’essaie de corriger deux avertissements lors de la compilation d’un programme spécifique à l’aide de GCC. Les avertissements sont:

warning: le déréférencement du pointeur typé va enfreindre les règles de crénelage ssortingct [-Wssortingct-aliasing]

et les deux coupables sont:

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

et

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

incoming_buf et outgoing_buf sont définis comme suit:

 char incoming_buf[LIBIRC_DCC_BUFFER_SIZE]; char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE]; 

Cela semble légèrement différent des autres exemples de cet avertissement que j’ai examiné. Je préférerais résoudre le problème plutôt que de désactiver les vérifications de crénelage ssortingct.

Il y a eu de nombreuses suggestions pour utiliser un syndicat – quel pourrait être le syndicat qui conviendrait dans ce cas?

Tout d’abord, examinons pourquoi vous obtenez les avertissements de violation de repliement de spectre.

Les règles de création d’alias indiquent simplement que vous ne pouvez accéder à un object que par son propre type, son type variant signé / non signé ou par un type de caractère ( char , signed char , unsigned char ).

C dit que la violation des règles de crénelage invoque un comportement indéfini ( alors ne le faites pas! ).

Dans cette ligne de votre programme:

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

bien que les éléments du tableau incoming_buf soient de type char , vous y accédez en tant que unsigned int . En effet, le résultat de l’opérateur de déréférence dans l’expression *((unsigned int*)dcc->incoming_buf) est de type unsigned int .

Ceci est une violation des règles de crénelage, car vous avez uniquement le droit d’accéder aux éléments du tableau incoming_buf via (voir le résumé des règles ci-dessus!) char , signed char ou unsigned char .

Notez que vous avez exactement le même problème d’aliasing dans votre deuxième coupable:

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

Vous accédez aux éléments char de outgoing_buf via unsigned int , il s’agit donc d’une violation d’alias.

Solution proposée

Pour résoudre votre problème, vous pouvez essayer de définir directement les éléments de vos tableaux dans le type auquel vous souhaitez accéder:

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

(D’ailleurs, la largeur de unsigned int est définie par l’implémentation. Vous devriez donc envisager d’utiliser uint32_t si votre programme suppose que unsigned int est 32 bits).

De cette façon, vous pouvez stocker unsigned int objects unsigned int dans votre tableau sans violer les règles de crénelage en accédant à l’élément via le caractère char , comme ceci:

 *((char *) outgoing_buf) = expr_of_type_char; 

ou

 char_lvalue = *((char *) incoming_buf); 

MODIFIER:

J’ai entièrement retravaillé ma réponse, en particulier j’explique pourquoi le programme obtient les avertissements de repliement du compilateur.

Pour résoudre le problème, ne pas punir et alias ! La seule façon “correcte” de lire un type T consiste à allouer un type T et à renseigner sa représentation si nécessaire:

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

En bref: si vous voulez un entier, vous devez en faire un entier. Il n’y a aucun moyen de sortingcher avec cela d’une manière tolérée par la langue.

La seule conversion de pointeur autorisée (pour les besoins d’E / S en général) consiste à traiter l’adresse d’ une variable existante de type T comme un caractère char* , ou plutôt comme le pointeur sur le premier élément d’un tableau de caractères. de taille 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)); 

Explication simplifiée 1. La norme c ++ indique que vous devez essayer d’aligner vous-même les données. G ++ fait encore plus pour générer des avertissements sur le sujet. 2. vous ne devriez essayer que si vous comprenez parfaitement l’alignement des données sur votre architecture / système et à l’intérieur de votre code (par exemple, le code ci-dessus est une chose sûre sur Intel 32/64; alignement 1; Win / Linux / Bsd / Mac) 3. la seule raison pratique d’utiliser le code ci-dessus est d’éviter les avertissements du compilateur, QUAND et SI vous savez ce que vous faites

Si je peux me permettre, à mon humble avis, dans ce cas, le problème est la conception des API ntohl et htonl et des fonctions associées. Ils n’auraient pas dû être écrits sous forme d’argument numérique avec retour numérique. (et oui, je comprends le point d’optimisation macro) Ils auraient dû être conçus comme le côté “n” étant un pointeur sur un tampon. Lorsque cela est fait, tout le problème disparaît et la routine est précise, quel que soit le système hôte de l’hôte. Par exemple (sans tentative d’optimisation):

 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; }; 

Placez le pointeur sur non signé, puis sur le pointeur.

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