2017-07-06 4 views
3

Je sais, je l'ai vu déjà, mais je ne pouvais trouver aucune bonne explication pourquoi pas ce comportement:Comment ce comportement d'alias strict est-il indéfini?

#include <stdio.h> 
#include <stdint.h> 

//Common union for both types 
union float_int { 
    float f; 
    uint32_t i; 
}; 

int main(void) { 
    union float_int fi; 
    //This should be problematic 
    uint32_t* i_ptr = (uint32_t *)&fi.f; 

    fi.f = 10.0f; 
    printf("%f : %u\r\n", fi.f, fi.i); //Prints: 10.000000 : 1092616192 which is OK 
    printf("%u\r\n", *i_ptr); //Prints: 1092616192 which is also OK 

    return 0; 
} 

Si nous vérifions la représentation de la mémoire, les deux sont 4-bytes longtemps donc il n'y a pas de débordement de mémoire dans pointage ou similaire.

Comment est ce comportement indéfini?

int main() { 
    union float_int fi; 
    void* v_ptr = &fi.f; 
    uint32_t* i_ptr = (uint32_t *)v_ptr; 
} 

Ce code est-il encore un comportement non défini? Je veux lire float nombre comme unsigned integer 32-bits. Pourquoi utiliser memcpy est le seul moyen de le faire? Pourquoi utiliser memcpy?

+0

'indefined' ->' undefined'? –

+0

@SouravGhosh Oui, merci. – tilz0R

+0

@Stargateur donnez-moi une réponse correcte, s'il vous plaît. Je ne pouvais pas le trouver. – tilz0R

Répondre

1

Ce n'est pas un aliasing strict, il s'agit d'une violation de strict aliasing.

D'abord, vous faites

uint32_t* i_ptr = (uint32_t *)&fi.f; //converting to a non-character type pointer 

puis, vous essayez d'accéder que par

printf("%u\r\n", *i_ptr); //access value via incompatible lvalue expr. 

qui provoque le problème. float et uint32_t sont pas types compatibles.

C11 Citant, chapitre §6.5/P7

Un objet doit avoir sa valeur stockée accessible uniquement par une expression lvalue qui est l'un des les types suivants: 88)

- un type compatible avec le type effectif de l'objet,

- une version homologuée d'un type compatible avec le type effectif de l'objet,

- un type qui est la signature ou le type non signé correspondant au type effective de l'objet ,

- un type qui est la signature ou le type non signé correspondant à une version admissible de la Type effective de l'objet ,

- un type agrégat ou de l'union qui comprend l'un des types précités, parmi ses membres (y compris, de façon récursive, un membre d'un sous-agrégat ou union contenu), ou

- un type de caractère.


En réponse au commentaire, nous allons voir C11, chapitre §6.2.6.1

valeurs stockées dans les objets de champ non-bit de tout autre type d'objet sont constitués de n × CHAR_BIT bits, où n est la taille d'un objet de ce type, en octets. La valeur peut être copiée dans un objet de type unsigned char [n] (par exemple, par memcpy); l'ensemble des octets qui en résulte est appelé la représentation de l'objet de la valeur.

et

Certaines représentations d'objet doivent pas être considerées une valeur du type d'objet. Si la valeur stockée d'un objet a une telle représentation et est lue par une expression lvalue qui n'a pas de type de caractère , le comportement n'est pas défini. [...] Une telle représentation est appelée une représentation de trap.

+0

Je suis d'accord qu'ils ne le sont pas, mais les deux ont une longueur de 4 octets et tous deux pointent vers la même mémoire. Le CPU ne se soucie pas de ce qui est là, c'est juste comme vous le lisez et l'interprétez. Étrange cependant pour moi. Et seulement 'memcpy' pourrait résoudre ceci. Encore plus étrange. Merci quand même. – tilz0R

+1

@ tilz0R OK, laissez-moi développer. –

+0

Espérons que je trouve un casting de tampon dans une réponse. Situation similaire. Merci pour la mise à jour. – tilz0R

0

Les représentations de flottant et d'entier sont différentes les unes des autres. C'est pourquoi en définissant une telle union, ce n'est pas un bon cas d'utilisation.

Dans votre exemple, vous effectuez la conversion de void * en uint32_t *. La conversion est effectuée au niveau du pointeur. Cela signifie que i_ptr pointe vers un emplacement en mémoire, vu comme un interger, sans changer les bits eux-mêmes.

En résumé, si vous souhaitez que cette conversion fonctionne, vous devez modifier la représentation interne de la variable. Par exemple:

printf("%u\r\n", (uint32_t)*i_ptr); 
+0

"définir une union, telle que ... n'est pas possible": cela n'a pas de sens. Il semblerait qu'aucune union n'est possible du tout! –

+0

@YvesDaoust d'accord, je l'ai reformulé pour être plus précis – drorco