2010-11-16 21 views
14

Si je déclare une Union:Comment savoir quelle variable de Union est utilisée?

union TestUnion 
{ 
    struct 
    { 
     unsigned int Num; 
     unsigned char Name[5]; 
    }TestStruct; 
    unsigned char Total[7]; 
}; 

Maintenant, Comment puis-je savoir que si total [7] est utilisé ou TestStruct est utilisé?

J'utilise C! Je revisitais les syndicats et les structures et cette question m'est venue à l'esprit. "sizeof" ne peut pas être utilisé car les deux sont de la même taille, c'est-à-dire 7 octets. (Et voici une autre question)

Lorsque j'ai rempli seulement "Total" avec un caractère "a" et j'ai essayé sizeof(TestUnionInstance), il a retourné 12 (Taille de Char est 1 octet, droit?). J'ai donc isolé la structure et trouvé que la taille de la structure est de 12 octets, pas 5 + 2 = 7 octets .... Étrange !! Tout le monde peut expliquer ??

P.S. J'utilise Visual Studio 2008.

Répondre

20

Vous ne pouvez pas. Cela fait partie du point de vue des syndicats.

Si vous devez être capable de le dire, vous pouvez utiliser ce que l'on appelle une union étiquetée. Certaines langues ont un support intégré pour celles-ci, mais en C, vous devez le faire vous-même. L'idée est d'inclure une balise avec l'union que vous pouvez utiliser pour dire de quelle version il s'agit. Comme:

enum TestUnionTag {NUM_NAME, TOTAL}; 

struct { 
    enum TestUnionTag tag; 
    union { 
     struct { 
      unsigned int Num; 
      unsigned char Name[5]; 
     } TestStruct; 
     unsigned char Total[7]; 
    } value; 
} TestUnion; 

Puis dans votre code, vous assurez-vous toujours définir la variable de dire comment le syndicat est utilisé. A propos de sizeof: la structure est de 12 octets car il y a 4 octets pour l'int (les compilateurs les plus modernes ont un int de 4 octets, identique à un int long), puis trois octets de remplissage et cinq octets pour le caractères (je ne sais pas si le rembourrage vient avant ou après les caractères). Le remplissage est là pour que la structure soit un nombre entier de mots, de sorte que tout en mémoire reste aligné sur les limites des mots. Comme la structure a une longueur de 12 octets, l'union doit avoir une longueur de 12 octets; l'union ne change pas de taille selon ce qu'il y a dedans.

+0

Un merci spécial pour "Padding Part" .... Je ne le savais pas !! Génial!! – Swanand

+2

Le rembourrage vient après le 'char', pas avant. Strictement parlant, une implémentation * pourrait * mettre le remplissage avant elle, mais il faudrait alors mettre la même quantité de remplissage avant si vous avez changé de 'char [5]' à 'char [6]', ce qui serait assez absurde . C nécessite des structures avec une séquence initiale commune d'éléments pour être compatible. –

1

Il n'y a aucun moyen de le dire. Vous devriez avoir des drapeaux supplémentaires (ou d'autres moyens externes à votre syndicat) indiquant quelles parties de l'union sont vraiment utilisées.

6

Le membre à utiliser est celui auquel vous avez écrit pour la dernière fois; l'autre (s) sont hors limites. Vous savez à quel membre vous avez écrit pour la dernière fois, n'est-ce pas? Après tout, c'est toi qui a écrit le programme :-)


Quant à vous question secondaire: le compilateur est autorisé à insérer « octets de remplissage » dans la structure pour éviter les accès non alignés et de le rendre plus performant .

example of a possible distribution of bytes inside your structure 

Num |Name  |pad 
- - - -|- - - - -|x x x 
0 1 2 3|4 5 6 7 8|9 a b 
+1

+1 pour avoir pris le temps d'afficher la disposition de la mémoire de l'objet. –

+1

En fait, parfois vous avez juste besoin d'accéder aux autres membres. Une fonction importante des syndicats est de fournir des points de vue différents aux mêmes données. – thkala

+1

En ce qui concerne le commentaire "hors-limites", il convient de noter que pour le compilateur, rien n'est interdit. Le programmeur doit appliquer manuellement de telles politiques. – thkala

2

D'abord, sizeof(int) la plupart des architectures va être de nos jours 4. Si vous voulez 2, vous devriez regarder short ou int16_t dans l'en-tête stdint.h dans C99 si vous voulez être précis.

Deuxièmement, C utilise des octets de remplissage pour s'assurer que struct est aligné sur une limite de mot (4). Donc, votre structure ressemble à ceci:

+---+---+---+---+---+---+---+---+---+---+---+---+ 
|  Num  | N a m e | | | | 
+---+---+---+---+---+---+---+---+---+---+---+---+ 

Il y a 3 octets à la fin.Sinon, le prochain struct dans un tableau aurait son champ Num dans un endroit maladroitement aligné, ce qui le rendrait moins efficace d'accès.

Troisièmement, le sizeof une union va être le sizeof c'est le plus grand membre. Même si tout cet espace n'est pas utilisé, sizeof va retourner le plus grand résultat.

Vous avez besoin, comme d'autres réponses ont mentionné, d'une autre manière (comme un enum) pour déterminer quel champ de votre union est utilisé.

4

Réponse courte: il n'y a pas d'autre solution que d'ajouter une énumération quelque part dans votre structure en dehors de l'union.

enum TestUnionPart 
{ 
    TUP_STRUCT, 
    TUP_TOTAL 
}; 

struct TestUnionStruct 
{ 
    enum TestUnionPart Part; 
    union 
    { 
    struct 
    { 
     unsigned int Num; 
     unsigned char Name[5]; 
    } TestStruct; 
    unsigned char Total[7]; 
    } TestUnion; 
}; 

Maintenant, vous aurez besoin de contrôler la création de votre syndicat pour vous assurer que le ENUM est correctement réglée, par exemple avec des fonctions similaires à:

void init_with_struct(struct TestUnionStruct* tus, struct TestStruct const * ts) 
{ 
    tus->Part = TUP_STRUCT; 
    memcpy(&tus->TestUnion.TestStruct, ts, sizeof(*ts)); 
} 

Dispatch sur les valeurs correctes est maintenant un commutateur:

void print(struct TestUnionStruct const * tus) 
{ 
    switch (tus->Part) 
    { 
    case TUP_STRUCT: 
     printf("Num = %u, Name = %s\n", 
      tus->TestUnion.TestStruct.Num, 
      tus->TestUnion.TestStruct.Name); 
     break; 
    case TUP_TOTAL: 
     printf("Total = %s\n", tus->TestUnion.Total); 
     break; 
    default: 
     /* Compiler can't make sure you'll never reach this case */ 
     assert(0); 
    } 
} 

Comme une note de côté, je voudrais mentionner que ces constructions sont mieux poignée d dans les langues de la famille ML.

type test_struct = { num: int; name: string } 
type test_union = Struct of test_struct | Total of string