web-dev-qa-db-fra.com

Différence entre une structure et une union

Y at-il un bon exemple pour faire la différence entre un struct et un union? En gros, je sais que struct utilise toute la mémoire de son membre et union utilise l’espace mémoire le plus grand. Existe-t-il une autre différence de niveau de système d'exploitation?

385
gagneet

Avec une union, vous n'êtes censé utiliser que l'un 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. Une structure, par contre, 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 réglage de x.b sur 'c' change la valeur de x.a, techniquement parlant, il n'est pas défini. Sur la plupart des machines modernes, un caractère correspond à 1 octet et un entier à 4 octets. Par conséquent, si vous attribuez à x.b la valeur "c", le premier octet de x.a a la même valeur:

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

empreintes

99, 99

Pourquoi les deux valeurs sont-elles les mêmes? Parce que les 3 derniers octets de l'int 3 sont tous égaux à zéro, on lit donc aussi 99. Si nous mettons un plus grand nombre pour x.a, 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);

empreintes

387427, 99

Pour examiner 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);

empreintes

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é 0xEF avec 0x22 sur mon Mac, mais il existe d’autres plates-formes où écraserait le 0xDE à la place parce que l'ordre des octets qui composent l'int était inversé. Par conséquent, lors de l'écriture d'un programme, vous ne devez jamais vous fier à l'écrasement de données spécifiques dans une union, car elles ne sont pas portables.

Pour plus de lecture sur la commande des octets, consultez endianness .

643
Kyle Cronin

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 obliger le compilateur à remplir 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)), encore une fois avec la possibilité d'ajouter des éléments pour les alignements.

77
Charlie Martin

Y a-t-il un bon exemple pour faire la différence entre une "structure" 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é précisé que, en fonction 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 à un même emplacement de stockage de représenter plusieurs types de données, garantissant que vous ne souhaiterez stocker qu'un seul type de données à la fois.

Les syndicats 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 "superposés" sont parfois utilisés de cette manière. Vous pouvez parfois utiliser des unions pour économiser de la mémoire lorsque vous avez une structure de données dans laquelle un seul type parmi plusieurs sera sauvegardé à la fois.

En général, le système d'exploitation ne s'intéresse pas aux structures et aux unions - il ne s'agit que de blocs de mémoire. Une structure est un bloc de mémoire qui stocke plusieurs objets de données, ces objets ne se chevauchant pas. Une union est un bloc de mémoire qui stocke plusieurs objets de données, mais ne stocke que pour le plus grand d'entre eux et ne peut donc stocker qu'un seul des objets de données à la fois.

49
cygil

Comme vous l'avez déjà indiqué dans votre question, la différence principale entre union et struct est que les membres union se superposent de manière à ce que la taille d'une union soit la même, alors que les membres struct se superposent (avec possibilité de remplissage facultatif). entre). En outre, un syndicat est assez grand pour contenir tous ses membres et avoir un alignement qui convient à tous ses membres. Supposons donc que int ne puisse être stocké qu’à des adresses de 2 octets, qu’il ait une largeur de 2 octets et qu’il soit long, qu’il ne puisse être stocké qu’à des adresses de 4 octets et à une longueur de 4 octets. L'union suivante

union test {
    int a;
    long b;
}; 

pourrait avoir un sizeof de 4 et une exigence d'alignement de 4. Une union et une structure peuvent avoir un remplissage à la fin, mais pas à leur début. L'écriture dans une structure ne change que la valeur du membre écrit. Ecrire à un membre d'un syndicat annulera la valeur de tous les autres membres. Vous ne pouvez pas y accéder si vous ne les avez pas écrites 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, peu importe que le programme utilisateur écrit dans une union ou dans une structure. Ce n'est en réalité qu'un problème du compilateur.

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

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

some_test_pointer peut pointer sur int* ou double*. Si vous transformez une adresse de type test en int*, elle pointera vers son premier membre, a, en fait. La même chose est vraie pour un syndicat aussi. Ainsi, comme un syndicat aura toujours le bon alignement, vous pouvez utiliser un syndicat pour que le pointage sur un type soit valide:

union a {
    int a;
    double b;
};

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

La valeur stockée d'un objet doit être accessible uniquement par une expression lvalue qui possède l'un des types suivants:

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

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

39

Une union est utile dans quelques scénarios. union peut être un outil de manipulation de bas niveau, comme l'écriture de pilotes de périphériques pour un noyau.

Un exemple de cela consiste à disséquer un nombre float en utilisant union d'un struct avec des champs de bits et un float. Je garde un numéro dans la float et plus tard, je peux accéder à des parties particulières de la float par cette struct. L'exemple montre comment union est utilisé pour avoir différents angles pour examiner 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 oeil à simple précision description sur wikipedia. J'ai utilisé l'exemple et le nombre magique 0.15625 à partir de là.


union peut également être utilisé pour implémenter un type de données algébrique comportant plusieurs alternatives. J'ai trouvé un exemple de cela dans le livre "Real World Haskell" de O'Sullivan, Stewart et Goerzen. Vérifiez-le dans la section L'union discriminée .

À votre santé!

18
Krzysztof Voss

Oui, la principale différence entre struct et union est la même que celle que vous avez indiquée. Struct utilise toute la mémoire de ses membres et l'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 signal à la fois. La déclaration générale sera donc:

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

Dans ce cas, utilisez 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. Cela fait beaucoup de différence.

Pour résumer, vous devez sélectionner Union si vous savez que vous accédez à l'un des membres à la fois.

11
Ravi Kanth

"nion" et "struct" sont des constructions du langage C. Parler d'une différence de "niveau OS" entre eux est inapproprié, car c'est le compilateur qui produit un code différent si vous utilisez l'un ou l'autre mot clé.

11
Gabriele D'Antona

Non techniquement parlant signifie:

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

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

nion: S'il y a 3 personnes ne seule chaise sera là pour s'asseoir, tous devront utiliser la même chaise lorsqu'ils voudront s'asseoir.

Techniquement parlant signifie:

Le programme mentionné ci-dessous donne un aperçu complet de la structure et de l'union.

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

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

Union obtient un bloc de mémoire du membre de taille maximale (ici, ses 32 bits). À l'intérieur de l'union, une structure supplémentaire (INNER_STRUCT) contient un bloc de mémoire d'une taille totale de 32 bits (16 + 8 + 8). En union, il est possible d’accéder au membre INNER_STRUCT (32 bits) o données (32 bits).

11
skanda93

Vous l'avez, c'est tout. Mais alors, en gros, à quoi servent les syndicats?

Vous pouvez mettre dans le même emplacement du 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 struct avec une balise de type ...).

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

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

10
Piotr Lesnicki

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

Une union n'alloue que la quantité de mémoire requise par son membre le plus important.

9
CMS

Les utilisations du syndicat Les syndicats sont fréquemment utilisés lorsque des conversations de type spécialisé sont nécessaires. Se faire une idée de l'utilité de l'union. La bibliothèque standard c/c ne définit aucune fonction spécialement conçue pour écrire des entiers courts dans un fichier. L'utilisation de fwrite () entraîne une surcharge excessive pour une opération simple. Cependant, en utilisant une union, vous pouvez facilement créer une fonction qui écrit le binaire d'un entier court dans un fichier, octet par octet. 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 () j’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é

3
Ahmed

quelle est la difference entre structure et union?

La réponse abrégée est: la déférence est dans l’allocation de mémoire. Explanation: Dans la structure, l'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 ayant besoin du plus grand espace mémoire. Considérons 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 dans struct et union: int et long int. Espace mémoire pour int est: 4 octets et espace mémoire pour int long est: 8 dans le système d'exploitation 32 bits.

Donc pour struct 4 + 8 = 12 octets seront créés alors que 8 octets seront créés pour 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/

3

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

nous utilisons habituellement 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 présente, car elle n’obtient qu’un seul bloc de mémoire, qui est égal au type le plus gros.

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

mémoire totale, il obtient => 5 octets

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

mémoire totale, il obtient = 4 octets

3
Anurag Bhakuni

Une Union est différente d'une structure telle qu'elle se répète sur les autres: elle redéfinit la même mémoire, tandis que la structure se définit les unes après les autres, sans chevauchement ni redéfinition.

1
Dennis Ng

Les syndicats sont utiles lors de l’écriture d’une fonction de classement des octets décrite ci-dessous. Ce n'est pas possible avec les structs.

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.
1