Différence entre une structure et une union

412

Y a-t-il un bon exemple pour donner la différence entre a structet a union? Fondamentalement, je sais questruct utilise toute la mémoire de son membre et unionutilise le plus grand espace mémoire des membres. Y a-t-il une autre différence de niveau du système d'exploitation?

gagneet
la source

Réponses:

678

Avec une union, vous n'êtes censé utiliser qu'un seul des éléments, car ils sont tous stockés au même endroit. Cela le rend utile lorsque vous souhaitez stocker quelque chose qui pourrait être de plusieurs types. Un struct, d'autre part, a un emplacement de mémoire séparé pour chacun de ses éléments et ils peuvent tous être utilisés en même temps.

Pour donner un exemple concret de leur utilisation, je travaillais sur un interpréteur Scheme il y a quelque temps et je superposais essentiellement les types de données Scheme sur les types de données C. Cela impliquait de stocker dans une structure une énumération indiquant le type de valeur et une union pour stocker cette 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 à quel point xb est réglé sur 'c', la valeur de xa change, techniquement parlant, cela n'est pas défini. Sur la plupart des machines modernes, un char est de 1 octet et un int de 4 octets, donc donner à xb la valeur 'c' donne également le premier octet de xa à la même valeur:

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

impressions

99, 99

Pourquoi les deux valeurs sont-elles identiques? Parce que les 3 derniers octets de l'int 3 sont tous à zéro, il est donc également lu comme 99. Si nous mettons un plus grand nombre pour xa, 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);

impressions

387427, 99

Pour voir de plus près les valeurs réelles de la mémoire, définissons et imprimons les valeurs en hexadécimal:

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

impressions

deadbe22, 22

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

MAIS

En C, l'ordre des octets dans un int n'est pas défini. Ce programme a remplacé le 0xEF par 0x22 sur mon Mac, mais il existe d'autres plates-formes où il remplacerait le 0xDE à la place car l'ordre des octets qui composent l'int a été inversé. Par conséquent, lors de l'écriture d'un programme, vous ne devez jamais vous fier au comportement d'écrasement de données spécifiques dans une union car il n'est pas portable.

Pour plus d'informations sur l'ordre des octets, consultez l' endianness .

Kyle Cronin
la source
1
en utilisant cet exemple, en union, si xb = 'c' qu'est-ce qui est stocké dans xa? est-ce le numéro de référence du caractère?
kylex
1
j'espère que cela explique plus en détail ce qui est stocké dans xa lorsque vous définissez xb
Kyle Cronin
1
@KyleCronin Je pense que je comprends. Dans votre cas, vous avez un groupe de types, sachant que vous n'aurez besoin d'en utiliser qu'un, mais vous ne savez pas lequel avant l'exécution - l'union vous permet de le faire. Merci
user12345613
2
@ user12345613 les unions peuvent être utilisées comme une sorte de classe de base pour les structures. Vous pouvez émuler une hiérarchie OO en utilisant des unions de structures
Morten Jensen
1
L'ordre des octets @Lazar dans les types multi-octets dépend de l'endianité. Je suggère de lire l'article Wikipedia à ce sujet.
Kyle Cronin
83

Voici la réponse courte: une structure est une structure d'enregistrement: chaque élément de la structure alloue un nouvel espace. Donc, 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))octets en mémoire pour chaque instance. ("Au moins" car les contraintes d'alignement de l'architecture peuvent forcer le compilateur à remplir la structure.)

D'autre part,

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

alloue un morceau de mémoire et lui donne quatre alias. Donc sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)), encore une fois avec la possibilité d'un ajout pour les alignements.

Charlie Martin
la source
53

Existe-t-il un bon exemple pour faire la différence entre une «struct» et une «union»?

Un protocole de communication imaginaire

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

Dans ce protocole imaginaire, il a été spécifié que, sur la base du "type de message", l'emplacement suivant dans l'en-tête sera soit un numéro de demande, soit un code à quatre caractères, mais pas les deux. En bref, les unions permettent au même emplacement de stockage de représenter plusieurs types de données, où il est garanti que vous ne souhaiterez stocker qu'un seul des types de données à la fois.

Les unions sont en grande partie un détail de bas niveau basé sur l'héritage de C en tant que langage de programmation système, où des emplacements de stockage «se chevauchant» sont parfois utilisés de cette manière. Vous pouvez parfois utiliser des unions pour économiser de la mémoire où vous avez une structure de données où un seul parmi plusieurs types sera enregistré à la fois.

En général, le système d'exploitation ne se soucie pas ou ne connaît pas les structures et les unions - ce ne sont que des blocs de mémoire. Un struct est un bloc de mémoire qui stocke plusieurs objets de données, où ces objets ne se chevauchent pas. Une union est un bloc de mémoire qui stocke plusieurs objets de données, mais n'a de stockage que pour les plus grands d'entre eux, et ne peut donc stocker qu'un seul des objets de données à la fois.

cygile
la source
1
Oui. Cela explique bien un cas d'utilisation!
gideon
1
Supposons que vous ayez un packetheader ph;accès au numéro de demande. ph.request.requestnumber?
justin.m.chase
Meilleure explication! Merci.
84RR1573R
39

Comme vous l'avez déjà dit dans votre question, la principale différence entre unionet structest que les unionmembres se superposent la mémoire afin que la taille d'une union soit la même, tandis que les structmembres sont disposés les uns après les autres (avec un remplissage facultatif entre les deux). Un syndicat est également assez grand pour contenir tous ses membres et avoir un alignement qui convient à tous ses membres. Disons que intne peut être stocké qu'à des adresses de 2 octets et a une largeur de 2 octets, et long ne peut être stocké qu'à des adresses de 4 octets et est de 4 octets de long. L'union suivante

union test {
    int a;
    long b;
}; 

pourrait avoir une valeur sizeofde 4 et une exigence d'alignement de 4. Une union et une structure peuvent avoir un remplissage à la fin, mais pas au début. L'écriture dans une structure modifie uniquement la valeur du membre sur lequel écrire. Écrire à un membre d'un syndicat rendra la valeur de tous les autres membres invalide. Vous ne pouvez pas y accéder si vous ne leur avez pas écrit auparavant, sinon le comportement n'est pas défini. GCC fournit une extension que vous pouvez réellement lire auprès des membres d'un syndicat, même si vous ne leur avez pas écrit récemment. Pour un système d'exploitation, il n'a pas d'importance si un programme utilisateur écrit dans une union ou dans une structure. Ce n'est en fait qu'un problème du compilateur.

Une autre propriété importante de l'union et de la structure est qu'ils permettent qu'un pointeur vers eux puisse pointer vers des types de n'importe lequel de ses membres . Donc, ce qui suit est valide:

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

some_test_pointer peut pointer vers int*ou double*. Si vous convertissez une adresse de type testen int*, elle pointera vers son premier membre, aen fait. Il en va de même pour un syndicat également. Ainsi, comme une union aura toujours le bon alignement, vous pouvez utiliser une union pour rendre le pointage vers un type valide:

union a {
    int a;
    double b;
};

Cette union pourra en fait 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 en fait valide, comme indiqué par la norme C99:

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

  • un type compatible avec le type effectif de l'objet
  • ...
  • un type d'agrégat ou d'union qui inclut l'un des types susmentionnés parmi ses membres

Le compilateur n'optimisera pas le v->a = 10;car il pourrait affecter la valeur de *some_int_pointer(et la fonction retournera 10au lieu de 5).

Johannes Schaub - litb
la source
18

A unionest utile dans quelques scénarios. unionpeut être un outil de manipulation de très bas niveau comme l'écriture de pilotes de périphérique pour un noyau.

Un exemple de cela est la dissection d'un floatnombre en utilisant unionun structavec des champs binaires et un float. J'enregistre un numéro dans le float, et plus tard je peux accéder à des parties particulières du floatvia struct. L'exemple montre comment unionutiliser différents angles pour regarder les 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 œil à la description simple précision sur wikipedia. J'ai utilisé l'exemple et le nombre magique 0,15625 à partir de là.


unionpeut également être utilisé pour implémenter un type de données algébrique qui a plusieurs alternatives. J'en ai trouvé un exemple dans le livre "Real World Haskell" de O'Sullivan, Stewart et Goerzen. Découvrez-le dans la section Le syndicat discriminé .

À votre santé!

Krzysztof Voss
la source
11

" union " et " struct " sont des constructions du langage C. Il n'est pas approprié de parler d'une différence de "niveau du système d'exploitation" entre eux, car c'est le compilateur qui produit un code différent si vous utilisez l'un ou l'autre mot-clé.

Gabriele D'Antona
la source
11

Non techniquement parlant signifie:

Hypothèse: chaise = bloc de mémoire, personnes = variable

Structure : S'il y a 3 personnes, elles peuvent s'asseoir sur une chaise de leur taille correspondante.

Union : S'il y a 3 personnes une seule chaise sera là pour s'asseoir, toutes doivent utiliser la même chaise quand elles veulent s'asseoir.

Techniquement parlant, cela signifie:

Le programme mentionné ci-dessous donne une plongée profonde dans la structure et l'union ensemble.

struct MAIN_STRUCT
{
UINT64 bufferaddr;   
union {
    UINT32 data;
    struct INNER_STRUCT{
        UINT16 length;  
        UINT8 cso;  
        UINT8 cmd;  
           } flags;
     } data1;
};

Taille totale MAIN_STRUCT = taille de (UINT64) pour bufferaddr + taille de (UNIT32) pour union + 32 bits pour le remplissage (dépend de l'architecture du processeur) = 128 bits. Pour la structure, tous les membres obtiennent le bloc de mémoire de manière contiguë.

L'union obtient un bloc de mémoire du membre de taille maximale (ici, son 32 bits). À l'intérieur de l'union, une structure supplémentaire se trouve (INNER_STRUCT), ses membres obtiennent un bloc de mémoire de taille totale 32 bits (16 + 8 + 8). En union, les membres INNER_STRUCT (32 bits) ou les données (32 bits) sont accessibles.

skanda93
la source
Grande explication. À votre santé!
Prem
11

Oui, la principale différence entre struct et union est la même que vous l'avez dit. Struct utilise toute la mémoire de son membre et union utilise le plus grand espace mémoire des membres.

Mais toute la différence réside dans le besoin d'utilisation de la mémoire. Le meilleur usage de l'union peut être vu dans les processus d'unix où nous utilisons des signaux. comme un processus ne peut agir que sur un seul signal à la fois. La déclaration générale sera donc:

union SIGSELECT
{
  SIGNAL_1 signal1;
  SIGNAL_2 signal2;
  .....
};

Dans ce cas, le processus utilise uniquement la mémoire la plus élevée de tous les signaux. mais si vous utilisez struct dans ce cas, l'utilisation de la mémoire sera la somme de tous les signaux. Ça fait beaucoup de différence.

Pour résumer, Union doit être sélectionné si vous savez que vous accédez à l'un des membres à la fois.

Ravi Kanth
la source
10

Vous l'avez, c'est tout. Mais alors, au fond, quel est l'intérêt des syndicats?

Vous pouvez mettre dans le même emplacement le contenu de différents types. Vous devez connaître le type de ce que vous avez stocké dans l'union (si souvent vous le mettez dans un structavec une étiquette de type ...).

Pourquoi est-ce important? Pas vraiment pour des gains d'espace. Oui, vous pouvez gagner des bits ou faire du padding, mais ce n'est plus le point principal.

C'est pour la sécurité des types, il vous permet de faire une sorte de `` typage dynamique '': le compilateur sait que votre contenu peut avoir des significations différentes et la signification précise de la façon dont votre interprétation le dépend de vous au moment de l'exécution. Si vous avez un pointeur qui peut pointer vers différents types, vous DEVEZ utiliser une union, sinon votre code peut être incorrect en raison de problèmes d'alias (le compilateur se dit "oh, seul ce pointeur peut pointer vers ce type, donc je peux optimiser ces accès ... ", et de mauvaises choses peuvent arriver).

Piotr Lesnicki
la source
9

Une structure alloue la taille totale de tous les éléments qu'elle contient.

Un syndicat n'alloue que la mémoire dont son plus grand membre a besoin.

CMS
la source
2
Vous pouvez également ajouter que les membres du syndicat se «superposent» en ce qu'ils commencent tous à l'adresse de début de la «structure» syndicale allouée.
Jim Buck
4

Quelle est la différence entre la structure et l'union?

La réponse abrégée est: La déférence est dans l'allocation de mémoire. Explication: Dans la structure, un espace mémoire sera créé pour tous les membres à l'intérieur de la structure. Dans l'union, l'espace mémoire sera créé uniquement pour un membre qui a besoin du plus grand espace mémoire. Considérez le code suivant:

struct s_tag
{
   int a; 
   long int b;
} x;

union u_tag
{
   int a; 
   long int b;
} y;

Ici, il y a deux membres à l'intérieur de struct et union: int et long int. L'espace mémoire pour int est: 4 octets et l'espace mémoire pour long int est: 8 dans le système d'exploitation 32 bits.

Donc, pour struct 4 + 8 = 12 octets seront créés tandis que 8 octets seront créés pour l'union

Exemple de code:

#include<stdio.h>
struct s_tag
{
  int a;
  long int b;
} x;
union u_tag
{
     int a;
     long int b;
} y;
int main()
{
    printf("Memory allocation for structure = %d", sizeof(x));
    printf("\nMemory allocation for union = %d", sizeof(y));
    return 0;
}

Réf: http://www.codingpractise.com/home/c-programming/structure-and-union/

Abdus Sattar Bhuiyan
la source
3

Les utilisations des unions syndicales sont fréquemment utilisées lorsque des conversations de type spécialisé sont nécessaires. Pour avoir une idée de l'utilité de l'union. La bibliothèque standard c / c ne définit aucune fonction spécifiquement conçue pour écrire des entiers courts dans un fichier. L'utilisation de fwrite () entraîne un surcoût excessif pour une opération simple. Cependant, en utilisant une union, vous pouvez facilement créer une fonction qui écrit un binaire d'un entier court dans un fichier un octet à la fois. Je suppose que les entiers courts ont une longueur de 2 octets

L'EXEMPLE:

#include<stdio.h>
union pw {
short int i;
char ch[2];
};
int putw(short int num, FILE *fp);
int main (void)
{
FILE *fp;
fp fopen("test.tmp", "wb ");
putw(1000, fp); /* write the value 1000 as an integer*/
fclose(fp);
return 0;
}
int putw(short int num, FILE *fp)
{
pw word;
word.i = num;
putc(word.c[0] , fp);
return putc(word.c[1] , fp);
}    

bien que putw () ai appelé avec un entier court, il était possible d'utiliser putc () et fwrite (). Mais je voulais montrer un exemple pour expliquer comment un syndicat peut être utilisé

Ahmed
la source
3

la structure est une collection de différents types de données où différents types de données peuvent y résider et chacun a son propre bloc de mémoire

nous avons généralement utilisé l'union lorsque nous sommes sûrs qu'une seule des variables sera utilisée à la fois et que vous souhaitez utiliser pleinement la mémoire actuelle car elle n'obtient qu'un seul bloc de mémoire égal au plus grand type.

struct emp
{
    char x;//1 byte
    float y; //4 byte
} e;

mémoire totale obtenue => 5 octets

union emp
{
    char x;//1 byte
    float y; //4 byte
} e;

mémoire totale obtenue = 4 octets

Anurag Bhakuni
la source
2

Les syndicats sont utiles lors de l'écriture d'une fonction de classement des octets qui est donnée ci-dessous. Ce n'est pas possible avec des structures.

int main(int argc, char **argv) {
    union {
        short   s;
        char    c[sizeof(short)];
    } un;

    un.s = 0x0102;

    if (sizeof(short) == 2) {
        if (un.c[0] == 1 && un.c[1] == 2)
            printf("big-endian\n");
        else if (un.c[0] == 2 && un.c[1] == 1)
            printf("little-endian\n");
        else
            printf("unknown\n");
    } else
        printf("sizeof(short) = %d\n", sizeof(short));

    exit(0);
}
// Program from Unix Network Programming Vol. 1 by Stevens.
Aniket Suryavanshi
la source
1

Une Union est différente d'une structure car une Union se répète sur les autres: elle redéfinit la même mémoire tandis que la structure définit l'une après l'autre sans chevauchements ni redéfinitions.

Dennis Ng
la source