442 votes

Différence entre une Structure et une Union en C

Y a-t-il un bon exemple de donner la différence entre un et un ? Fondamentalement, je sais que utilise toute la mémoire de ses membres et utilise le plus grand espace de mémoire de membres. Existe-t-il des autres différences niveau OS ?

718voto

Kyle Cronin Points 35834

Avec une union de fait, vous êtes supposé utiliser l'un des éléments, car ils sont tous stockés au même endroit. Ceci le rend utile lorsque vous souhaitez stocker quelque chose qui pourrait être de plusieurs types. Une structure, d'autre part, dispose d'un emplacement de mémoire pour chacun de ses éléments, et ils peuvent tous être utilisés à la fois.

Pour donner un exemple concret de leur utilisation, je travaillais sur un interpréteur Scheme il y a peu, et j'ai été essentiellement superposer le Schéma des types de données sur les types de données C. Ce stockage dans une struct une énumération indiquant le type de la valeur et de l'union pour mémoriser la valeur.

union foo {
  int a;   // can't use both a and b at once
  char b;
} foo;

struct bar {
  int a;   // can use both a and b simultaneously
  char b;
} bar;

union foo x;
x.a = 3; // OK
x.b = 'c'; // NO! this affects the value of x.a!

struct bar y;
y.a = 3; // OK
y.b = 'c'; // OK

edit: Si vous vous demandez quelle est la configuration de x.b 'c' modifie la valeur de x.une, techniquement parlant, il est indéfini. Sur la plupart des appareils modernes, un char est de 1 octet et un int fait 4 octets, afin de donner x.b la valeur de " c " donne aussi le premier octet de poids fort de x.une même valeur:

union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

imprime

99, 99

Pourquoi les deux valeurs de même? Parce que les 3 derniers octets de l'int 3 sont toutes à zéro, c'est donc aussi se lire comme 99. Si nous avons mis dans un plus grand nombre de x.un, vous verrez que ce n'est pas toujours le cas:

union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

imprime

387427, 99

À regarder de plus près les réelles valeurs de la mémoire, nous allons définir et d'imprimer les valeurs en hexadécimal:

union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);

imprime

deadbe22, 22

Vous pouvez voir clairement où le 0x22 a remplacé le 0xEF.

MAIS

En C, l'ordre des octets dans un int sont pas définis. Ce programme a remplacé la 0xEF avec 0x22 sur mon Mac, mais il y a d'autres plates-formes où il serait de remplacer la 0xDE plutôt parce que l'ordre des octets qui composent l'int étaient inversés. Par conséquent, lors de l'écriture d'un programme, vous ne devez jamais compter sur le comportement de l'écrasement des données spécifiques dans une union, car il n'est pas portable.

Pour plus de lecture sur l'ordre des octets, découvrez l'endianness.

87voto

Charlie Martin Points 62306

Voici la réponse courte: une struct est une structure d'enregistrement: chaque élément de la structure alloue un nouvel espace. Ainsi, une structure comme

struct foobarbazquux_t {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

alloue au moins (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double)) d'octets dans la mémoire de chaque instance. ("Au moins" parce que l'architecture d'alignement des contraintes peut forcer le compilateur à tampon de la structure.)

D'autre part,

union foobarbazquux_u {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

alloue un bloc de mémoire et lui donne quatre alias. Donc, sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)), à nouveau, avec la possibilité de plus pour les alignements.

59voto

cygil Points 2076

Est-il un bon exemple à donner la différence entre un "struct" et d'une "union"?

Un imaginaire protocole de communication

struct packetheader {
   int sourceaddress;
   int destaddress;
   int messagetype;
   union request {
       char fourcc[4];
       int requestnumber;
   };
};

Dans cet imaginaire protocole, il a été sepecified que, selon le "type de message", à l'emplacement suivant dans l'en-tête sera soit un numéro de demande, ou un code à quatre caractères, mais pas les deux. En bref, les syndicats de permettre le même emplacement de stockage pour représenter plus d'un type de données, où il est garanti que vous souhaitez stocker l'un des types de données à tout moment.

Les syndicats sont en grande partie le faible niveau de détail en fonction en C du patrimoine comme un langage de programmation système, où le "cumul" des emplacements de stockage sont parfois utilisés de cette façon. Vous pouvez parfois utiliser des syndicats pour économiser de la mémoire où vous avez une structure de données, où un seul des plusieurs types seront enregistrées à la fois.

En général, le système ne permet pas de soins ou de connaître les structures et les syndicats, ils sont à la fois tout simplement des blocs de mémoire. Une structure est un bloc de mémoire qui stocke des données de plusieurs objets, où ces objets ne se chevauchent pas. Un syndicat est un bloc de mémoire qui stocke des données de plusieurs objets, mais ne dispose que de stockage pour les plus grandes, et donc ne peut stocker qu'un des objets de données à tout moment.

39voto

Comme vous l'avez déjà état dans votre question, la principale différence entre union et struct que union des membres de la superposition de la mémoire de chaque autre, de sorte que le sizeof d'une union est l'un , en struct des membres sont disposés l'un après l'autre (avec l'option de rembourrage entre les deux). Aussi l'union est assez grand pour contenir tous ses membres, et d'avoir un alignement qui s'adapte à tous ses membres. Donc, disons int ne peuvent être stockés à 2 octets des adresses et des est de 2 octets de large et de long ne peut être conservé à 4 octets des adresses et long de 4 octets. L'union suivante

union test {
    int a;
    long b;
};

pourrait avoir un sizeof de 4, et un alignement de 4. À la fois une union et une structure peut avoir de remplissage à la fin, mais pas à leur début. L'écriture d'une structure ne change que la valeur du membre de l'écrit. Écrit à un membre d'une union rendre la valeur de tous les autres membres non valide. Vous ne pouvez pas y accéder si vous n'avez pas écrit avant, sinon le comportement est indéfini. GCC fournit une extension que vous pouvez lire à partir de membres de l'union, même si vous n'avez pas écrit plus récemment. Pour un Système d'Exploitation, il n'est pas question de savoir si un programme utilisateur écrit à un syndicat ou à une structure. En fait, ce n'est qu'une question de le compilateur.

Une autre propriété importante de l'union et de la structure est, ils permettent qu' un pointeur peut pointer vers des types de l'un de ses membres. Donc, la suivante est valide:

struct test {
    int a;
    double b;
} * some_test_pointer;

some_test_pointer peut point d' int* ou bool*. Si vous lancez une adresse de type test de int*, il ne sera point pour son premier membre, a, en fait. Le même est vrai pour une union trop. Ainsi, parce que l'union aura toujours le droit de l'alignement, vous pouvez utiliser un syndicat pour faire ressortir un certain nombre de type valide:

union a {
    int a;
    double b;
};

Cette union sera effectivement en mesure de pointer vers un int et un double:

union a * v = (union a*)some_int_pointer;
*some_int_pointer = 5;
v->a = 10;
return *some_int_pointer;

est valide de fait, comme l'a déclaré le standard C99:

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

  • un type compatible avec l'efficacité du type de l'objet
  • ...
  • une agrégation ou une union de type qui comprend l'un des types mentionnés ci-dessus, parmi ses membres,

Le compilateur ne pas optimiser l' v->a = 10; car cela pourrait affecter la valeur de *some_int_pointer (et la fonction retournera 10 au lieu de 5).

19voto

Krzysztof Voss Points 308

Je pense que des syndicats comme d'un outil pour très peu de niveau de la manipulation, comme l'écriture de pilotes de périphérique pour un noyau.

Vous posiez des questions sur un exemple et je pense que j'ai un excellent. Dans le code ci-dessous je suis la dissection de nombre à virgule en utilisant de l'union d'un struct avec bitfields et d'un flotteur. - Je enregistrer un numéro dans le flotteur, et, plus tard, je peux accéder à certaines parties de la flotte à travers la structure. Il montre comment l'union est utilisé pour avoir des angles différents, à regarder de données.

#include <stdio.h>                                                                                                                                       

union foo {
    struct float_guts {
        unsigned int fraction : 23;
        unsigned int exponent : 8;
        unsigned int sign     : 1;
    } fg;
    float f;
};

void print_float(float f) {
    union foo ff;
    ff.f = f;
    printf("%f: %d 0x%X 0x%X\n", f, ff.fg.sign, ff.fg.exponent, ff.fg.fraction);

}

int main(){
    print_float(0.15625);
    return 0;
}

Jetez un oeil à simple précision description sur wikipédia. J'ai utilisé le nombre magique 0.15625 à partir de là.

Cheers!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X