J'avais l'impression que l'accès à un union
membre autre que le dernier ensemble est UB, mais je n'arrive pas à trouver une référence solide (autre que des réponses affirmant que c'est UB mais sans aucun support de la norme).
Alors, est-ce un comportement non défini?
c++
undefined-behavior
language-lawyer
unions
Luchian Grigore
la source
la source
Réponses:
La confusion est que C autorise explicitement le poinçonnage de type via une union, alors que C ++ (c ++ 11) n'a pas une telle autorisation.
La situation avec C ++:
C ++ a plus tard un langage permettant l'utilisation d'unions contenant des
struct
s avec des séquences initiales communes; cela ne permet cependant pas le poinçonnage de type.Pour déterminer si la punition de type d'union est autorisé en C ++, nous devons chercher plus loin. Rappeler quec99 est une référence normative pour C ++ 11 (et C99 a un langage similaire à C11 permettant l'union de type-punning):
Cela devient particulièrement intéressant quand on lit
Ainsi, pour un type primitif (qui a ipso facto une initialisation triviale) contenu dans une union, la durée de vie de l'objet englobe au moins la durée de vie de l'union elle-même. Cela nous permet d'invoquer
En supposant que l'opération qui nous intéresse est le poinçonnage de type, c'est-à-dire qu'elle prend la valeur d'un membre syndical non actif, et que, conformément à ce qui précède, nous avons une référence valide à l'objet auquel ce membre fait référence, cette opération est lvalue-to -rvaleur conversion:
La question est alors de savoir si un objet qui est un membre syndical non actif est initialisé par stockage sur le membre syndical actif. Pour autant que je sache, ce n'est pas le cas et donc si:
char
stockage de la baie et inversement (3.9: 2), oul'accès à une union par un membre non actif est défini et est défini pour suivre la représentation de l'objet et de la valeur, l'accès sans l'une des interpositions ci-dessus est un comportement indéfini. Cela a des implications pour les optimisations autorisées à être effectuées sur un tel programme, car l'implémentation peut bien sûr supposer qu'un comportement indéfini ne se produit pas.
Autrement dit, bien que nous puissions légitimement former une lvalue à un membre syndical non actif (c'est pourquoi l'attribution à un membre non actif sans construction est acceptable), elle est considérée comme non initialisée.
la source
memcpy
implémentations personnalisées (accès aux objets à l'aide deunsigned char
lvalues), il interdisait les accès à*p
afterint *p = 0; const int *const *pp = &p;
(même si la conversion implicite deint**
àconst int*const*
est valide), il interdisait même l'accèsc
aprèsstruct S s; const S &c = s;
. Numéro 616 du CWG . Le nouveau libellé le permet-il? Il y a aussi [basic.lval].&
signifie l'opérateur unaire lorsqu'il est appliqué à un membre du syndicat. Je pense que le pointeur résultant devrait être utilisable pour accéder au membre au moins jusqu'à la prochaine utilisation directe ou indirecte de tout autre membre lvalue, mais dans gcc, le pointeur n'est pas utilisable même si longtemps, ce qui soulève la question de savoir quoi l'&
opérateur est censé vouloir dire.La norme C ++ 11 le dit de cette façon
Si une seule valeur est stockée, comment pouvez-vous en lire une autre? Ce n'est tout simplement pas là.
La documentation gcc le répertorie sous Comportement défini par l'implémentation
indiquant que cela n'est pas exigé par la norme C.
2016-01-05: Grâce aux commentaires, j'ai été lié au rapport de défaut C99 n ° 283 qui ajoute un texte similaire en tant que note de bas de page au document standard C:
Je ne sais pas si cela clarifie beaucoup, étant donné qu'une note de bas de page n'est pas normative pour la norme.
la source
Je pense que le plus proche de la norme vient de dire que son comportement indéfini est celui où il définit le comportement d'une union contenant une séquence initiale commune (C99, §6.5.2.3 / 5):
C ++ 11 donne des exigences / autorisations similaires au §9.2 / 19:
Bien que ni l'un ni l'autre ne le déclare directement, ces deux éléments impliquent fortement que "inspecter" (lire) un membre n'est "autorisé" que si 1) il fait (partie de) le membre le plus récemment écrit, ou 2) fait partie d'une initiale commune séquence.
Ce n'est pas une déclaration directe que faire autrement est un comportement indéfini, mais c'est le plus proche dont je suis conscient.
la source
union
pas définies, car un blog en particulier m'avait donné l'impression que c'était correct, et j'avais construit plusieurs grandes structures et projets autour de lui. Maintenant, je pense que ça ira peut-être après tout, puisque mesunion
s contiennent des classes ayant les mêmes types à l'avantunion
contient par exemple unuint8_t
et unclass Something { uint8_t myByte; [...] };
- je suppose que cette disposition s'appliquerait également ici, mais elle est formulée très délibérément pour n'autoriser que l'struct
art. Heureusement, j'utilise déjà ceux-ci au lieu des primitives brutes: OQuelque chose qui n'est pas encore mentionné par les réponses disponibles est la note de bas de page 37 au paragraphe 21 de la section 6.2.5:
Cette exigence semble clairement impliquer que vous ne devez pas écrire dans un membre et lire dans un autre. Dans ce cas, il peut s'agir d'un comportement indéfini par manque de spécification.
la source
J'explique bien cela avec un exemple.
supposons que nous ayons l'union suivante:
Je suppose bien que cela
sizeof(int)
donne 4, et celasizeof(short)
donne 2.quand vous écrivez
union A a = {10}
bien, créez une nouvelle var de type A en lui mettant la valeur 10.votre mémoire devrait ressembler à ça: (rappelez-vous que tous les membres du syndicat ont le même emplacement)
comme vous pouvez le voir, la valeur de ax est 10, la valeur de ay 1 est 10 et la valeur de ay [0] est 0.
maintenant, que se passe-t-il si je fais ça?
notre mémoire ressemblera à ceci:
cela transformera la valeur de ax à 2424842 (en décimal).
maintenant, si votre union a un flottant, ou double, votre carte mémoire sera bien plus en désordre, à cause de la façon dont vous stockez les nombres exacts. plus d'informations que vous pourriez obtenir ici .
la source