Accès à un membre inactif du syndicat et à un comportement non défini?

j'avais l'impression que l'accès à un membre union 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).

donc, est-ce un comportement non défini?

91
demandé sur jww 2012-07-07 11:37:00

5 réponses

la confusion est que C autorise explicitement le poinçonnage par type à travers une union, alors que C++ ( ) n'a pas cette autorisation.

6.5.2.3 Structure et membres du syndicat

95) si le membre utilisé pour lire le contenu d'un objet d'union n'est pas le même que le membre utilisé pour la dernière fois de stocker une valeur dans l'objet, la partie appropriée de la la représentation objet de la valeur est réinterprétée comme un objet de représentation dans le nouveau type décrit dans 6.2.6 (un processus parfois appelé ‘type beaucoup les jeux de mots’). Cela pourrait être un piège de la représentation.

la situation avec C++:

9.5 syndicats [classe.union]

dans un syndicat, au plus un des membres de données non statiques peut être actif à tout moment, c'est la valeur de la la plupart des données non statiques des membres peuvent être stockées dans un syndicat à tout moment.

C++ a plus tard un langage permettant l'utilisation de unions contenant struct s avec des séquences initiales communes; cela ne permet toutefois pas le type-punning.

pour déterminer si le type de syndicat est autorisé en C++, nous devons chercher plus loin. Rappelons que est une référence normative pour le C++11 (et C99 a similaires la langue de C11 permettant à l'union de type beaucoup les jeux de mots):

3.9 Types [de base.types]

4 - la représentation d'objet d'un objet de type T est la séquence de N objets char non signés repris par l'objet du type T, où N égale sizeof (T). La valeur de la représentation d'un objet est l'ensemble de bits maintenez la valeur du type T. pour les types trivialement copiables, la valeur de la représentation est un ensemble de bits dans l'objet représentation qui détermine une valeur, qui est un élément discret d'un ensemble de valeur. 42

42) l'intention est que le modèle de mémoire de C++ est compatible avec celui de ISO / IEC 9899 langage de programmation C.

c'est particulièrement intéressant quand on lit

3.8 durée de vie de L'objet [base.la vie]

la durée de vie d'un objet de type T commence lorsque: - un stockage avec un alignement et une taille appropriés pour le type T est obtenu, et - si l'objet a une initialisation non triviale, son initialisation est complète.

ainsi pour un type primitif (qui ipso facto a l'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

3.9.2 types composés [fondamental.composé]

si un objet de type T est situé à une adresse A, un pointeur de type cv T* dont la valeur est l'adresse A est censée pointer vers cet objet, quelle que soit la façon dont la valeur a été obtenue.

en supposant que l'opération qui nous intéresse est de type poinçonnage, c'est-à-dire en prenant la valeur d'un membre non actif de l'union, et donné par le ci-dessus, nous avons une référence valide à l'objet auquel se réfère ce membre, cette opération est la conversion de la valeur à la valeur:

4.1 L conversion de la valeur en valeur [conv.lval]

une valeur GL de type T peut être convertie en prvalue. Si T est un type incomplet, un programme qui nécessite cette conversion est mal formé. si l'objet auquel se réfère la glvalue est pas un objet de type T et n'est pas un objet d'un type dérivé de T , ou si l'objet est non initialisée, un programme qui nécessite cette conversion a un comportement indéfini.

la question est donc de savoir si un objet qui est un membre non actif du syndicat est initialisé par stockage au membre actif du syndicat. Pour autant que je puisse dire, ce n'est pas le cas et donc bien que si:

  • une union est copié dans char le stockage de réseau et le retour (3.9:2), ou
  • une union est bytewise copié à l'autre de l'union du même type (3.9:3), ou
  • une union est accessible au-delà des limites linguistiques par un élément de programme conforme à la norme ISO/IEC 9899 (dans la mesure où celle-ci est définie) (3.9:4 note 42), puis

l'accès à un syndicat par un membre non actif est défini et est défini pour suivre l'objet et la représentation des valeurs, l'accès sans l'une des interpositions ci-dessus est un comportement non défini. Cela a des implications pour les optimisations autorisées sur un tel programme, car la mise en œuvre peut naturellement supposer qu'un comportement non défini ne se produit pas.

c'est-à-dire que, bien que nous puissions légitimement former une valeur à un membre non actif du syndicat (ce qui est la raison pour laquelle l'assignation à un membre non actif sans construction est acceptable), elle est considérée comme non initialisée.

106
répondu ecatmur 2013-07-24 00:58:32

le standard C++11 le dit de cette façon

9.5 syndicats

Dans un syndicat, au plus l'un des non-membres de données statiques peut être actif à tout moment, c'est la valeur d'au plus l'un des non-membres de données statiques peuvent être stockées dans une union à tout moment.

Si une seule valeur est stockée, comment pouvez-vous lire un autre? Il n'est pas là.


la documentation du gcc indique ceci sous comportement défini par la mise en œuvre

  • un membre d'un objet de l'union est accessible en utilisant un membre d'un type différent (C90 6.3.2.3).

pertinentes octets de la représentation de l'objet sont traités comme un objet du type utilisé pour l'accès. Voir Type beaucoup les jeux de mots. Cela peut être un piège représentation.

indiquant que la norme C ne l'exige pas.


2016-01-05: dans les commentaires, j'ai été lié à C99 rapport de défaut n ° 283 qui ajoute un texte similaire comme note de bas de page au document standard C:

78a) si le membre utilisé pour accéder au contenu d'un objet syndical n'est pas le même que le membre utilisé en dernier lieu pour stocker un valeur dans l'objet, la partie appropriée de la représentation objet de la valeur est réinterprétée comme une représentation objet dans le nouveau type tel que décrit dans 6.2.6 (un processus parfois appelé "Type punning"). Cela pourrait être un piège de la représentation.

Je ne suis pas sûr qu'il clarifie beaucoup cependant, étant donné qu'une note de bas de page n'est pas normative pour la norme.

22
répondu Bo Persson 2016-01-05 11:48:37

je pense que la norme la plus proche de dire que c'est un comportement non défini est où il définit le comportement pour une union contenant une séquence initiale commune (C99, §6.5.2.3/5):

une garantie spéciale est constituée afin de simplifier l'utilisation des unions: si une union contient plusieurs structures qui partagent une même séquence initiale (voir ci-dessous), et si l'union objet contient actuellement l'une de ces structures, il est autorisé à inspecter les commun première partie de l'un d'eux partout où une déclaration complète du type de l'union est visible. Deux structures partagent une séquence initiale si les membres correspondants ont types compatibles (et, pour les champs de bits, les mêmes largeurs) pour une séquence d'un ou plusieurs les premiers membres.

C++11 donne des exigences similaires, permission au §9.2/19:

si une union-cadre contient deux ou plusieurs les structures de la norme-cadre qui partagent une séquence initiale commune, et si l'objet standard-layout union contient actuellement l'une de ces structures standard-layout, il est autorisé pour inspecter la partie initiale commune de l'un d'entre eux. Deux structures de la norme-cadre partagent une initiale commune séquence si les membres correspondants ont des types compatibles avec la mise en page et que ni l'un ni l'autre des membres n'est un champ bit ou les deux sont des champs bit avec la même largeur pour une séquence d'un ou plusieurs membres initiaux.

bien que ni l'un ni l'autre ne le déclare directement, ces deux termes impliquent fortement que" inspecter "(lire) un membre est" autorisé " seulement si 1) Il est (partie de) le membre le plus récemment écrit, ou 2) fait partie d'une séquence initiale commune.

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 au courant.

16
répondu Jerry Coffin 2012-08-10 18:06:46

ce qui n'est pas encore mentionné dans les réponses disponibles est la note de bas de page 37 du paragraphe 21 de la section 6.2.5:

noter que le type agrégé ne comprend pas le type union parce qu'un objet avec le type de syndicat ne peut contenir qu'un membre à la fois.

cette exigence semble impliquer clairement que vous ne devez pas écrire dans un membre et lire dans un autre. Dans ce cas, il pourrait être un comportement non défini par manque de spécification.

10
répondu mpu 2012-08-16 22:00:52

Je l'explique bien avec un exemple.

supposons que nous ayons l'union suivante:

union A{
   int x;
   short y[2];
};

je suppose bien que sizeof(int) donne 4, et que sizeof(short) donne 2.

quand vous écrivez union A a = {10} qui créent bien un nouveau var de type A dans mettez dans lui la valeur 10.

votre mémoire devrait ressembler à cela: (rappelez-vous que tous les membres du syndicat obtiennent le même endroit)

       |                   x                   |
       |        y[0]       |       y[1]        |
       -----------------------------------------
   a-> |0000 0000|0000 0000|0000 0000|0000 1010|
       -----------------------------------------

comme vous pouvez le voir, la valeur de A. x est 10, la valeur de A. y 1 is 10, and the value of a.y[0] est 0.

que se passera-t-il si je fais ça?

a.y[0] = 37;

notre mémoire ressemblera à ceci:

       |                   x                   |
       |        y[0]       |       y[1]        |
       -----------------------------------------
   a-> |0000 0000|0010 0101|0000 0000|0000 1010|
       -----------------------------------------

cela tournera la valeur de A. x to 2424842 (in decimal).

maintenant, si votre syndicat a un char, ou un double, votre carte mémoire bien plus d'un gâchis, car de la façon dont vous stockez des chiffres exacts. plus d'informations vous pouvez obtenir dans ici .

-2
répondu elyashiv 2014-09-14 09:20:19