Question

Je suis en train de fixer deux avertissements lors de la compilation d'un programme spécifique en utilisant GCC. Les mises en garde sont les suivants:

avertissement: déréférencement pointeur de type punned brisera Des règles strictes-aliasing [-Wstrict-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 subtilement différente de celle des autres exemples de cet avertissement que j'ai examinerai. Je préférerais résoudre le problème plutôt que de désactiver les contrôles stricts-aliasing.

Il y a eu de nombreuses suggestions d'utiliser une union - ce qui pourrait être une union adaptée à ce cas

Était-ce utile?

La solution

Tout d'abord, nous allons examiner pourquoi vous obtenez les avertissements de violation aliasing.

Aliasing règles dites simplement que vous ne pouvez accéder à un objet à travers son propre type, son type signé / variante non signé, ou par un type de caractère (char, signed char, unsigned char).

C dit avoir enfreint les règles d'aliasing invoque un comportement non défini ( pour ne pas ).

Dans cette ligne de votre programme:

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

bien que les éléments du tableau de incoming_buf sont de type char, vous les accédez comme unsigned int. En effet, le résultat de l'opérateur de déréférencement dans le *((unsigned int*)dcc->incoming_buf) d'expression est de type unsigned int.

Ceci est une violation des règles d'aliasing, parce que vous avez seulement le droit à des éléments d'accès du réseau d'incoming_buf par (voir règles résumé ci-dessus!) char, signed char ou unsigned char.

Notez que vous avez exactement le même problème de crénelage 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 par unsigned int, il est donc une violation aliasing.

Solution proposée

Pour résoudre votre problème, vous pouvez essayer d'avoir les éléments de vos tableaux définis directement dans le type que 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)];

(par la façon dont la largeur de unsigned int est mise en œuvre définie, vous devriez envisager d'utiliser uint32_t si votre programme prend unsigned int est de 32 bits).

De cette façon, vous pouvez stocker des objets unsigned int dans votre tableau sans violer les règles d'aliasing en accédant à l'élément à travers le char de type, comme ceci:

*((char *) outgoing_buf) =  expr_of_type_char;

ou

char_lvalue = *((char *) incoming_buf);

EDIT:

Je suis entièrement retravaillé ma réponse, en particulier, je vous expliquer pourquoi le programme obtient les avertissements d'aliasing du compilateur.

Autres conseils

Pour résoudre le problème, ne pas calembour et alias ! La seule « bonne » façon de lire un T de type est d'allouer un T de type et remplir sa représentation si nécessaire:

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

En bref: Si vous voulez un entier, vous devez faire un entier. Il n'y a pas moyen de tricher autour de cette façon de langage fermé les yeux.

La seule conversion de pointeur que vous êtes autorisé (à des fins de I / O, en général) est de traiter l'adresse une variable existante de type T comme char*, ou plutôt, comme le pointeur 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. c ++ états standard que vous devez tenter de vous aligner des données, g ++ va un mile supplémentaire pour générer des avertissements sur le sujet. 2. vous ne devriez l'essayer si vous comprenez complètement 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 que sur Intel 32/64, l'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, pour ce cas, le problème est la conception du ntohl et htonl et les API de fonction connexes. Ils ne doivent pas avoir été écrit comme argument numérique avec retour numérique. (Et oui, je comprends le point d'optimisation macro) Ils devraient avoir été conçu comme étant le côté « n » étant un pointeur vers un tampon. Lorsque cela est fait, le problème disparaît et la routine est exacte selon Endian l'hôte est. Par exemple (sans chercher à optimiser):

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 vous avez des raisons qui ne vous permettent pas de changer le type d'objet source (comme il était dans mon cas), et vous êtes absolument sûr que le code est correct et il fait ce que l'intention de faire avec ce tableau de caractères, à Évitez les avertissements que vous pouvez effectuer les opérations suivantes:

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

pointeur Cast unsigned et puis de nouveau pointeur.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top