Differenza tra una struttura e un'Unione


412

C'è qualche buon esempio per dare la differenza tra a structe a union? Fondamentalmente so che structutilizza tutta la memoria del suo membro e unionutilizza lo spazio di memoria dei membri più grandi. C'è qualche altra differenza di livello del sistema operativo?

Risposte:


678

Con un'unione, dovresti usare solo uno degli elementi, perché sono tutti memorizzati nello stesso punto. Questo lo rende utile quando si desidera memorizzare qualcosa che potrebbe essere di diversi tipi. Una struttura, d'altra parte, ha una posizione di memoria separata per ciascuno dei suoi elementi e tutti possono essere utilizzati contemporaneamente.

Per dare un esempio concreto del loro uso, qualche tempo fa stavo lavorando su un interprete Scheme e essenzialmente stavo sovrapponendo i tipi di dati Scheme ai tipi di dati C. Ciò ha comportato la memorizzazione in una struttura di un enum che indica il tipo di valore e un'unione per memorizzare quel valore.

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

modifica: se ti stai chiedendo a quale impostazione xb su 'c' cambi il valore di xa, tecnicamente parlando non è definito. Sulla maggior parte delle macchine moderne un carattere è 1 byte e un int è 4 byte, quindi dare a xb il valore 'c' dà anche al primo byte di xa lo stesso valore:

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

stampe

99, 99

Perché i due valori sono uguali? Poiché gli ultimi 3 byte di int 3 sono tutti zero, quindi viene letto anche come 99. Se inseriamo un numero maggiore per xa, vedrai che non è sempre così:

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

stampe

387427, 99

Per dare un'occhiata più da vicino ai valori di memoria effettivi, impostiamo e stampiamo i valori in esadecimale:

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

stampe

deadbe22, 22

Puoi vedere chiaramente dove 0x22 ha sovrascritto 0xEF.

MA

In C, l'ordine dei byte in un int non è definito. Questo programma ha sovrascritto 0xEF con 0x22 sul mio Mac, ma ci sono altre piattaforme in cui sovrascriverebbe 0xDE perché l'ordine dei byte che compongono l'int era invertito. Pertanto, quando si scrive un programma, non si dovrebbe mai fare affidamento sul comportamento di sovrascrivere dati specifici in un sindacato perché non è portatile.

Per ulteriori informazioni sull'ordinamento dei byte, consulta l' endianness .


1
usando questo esempio, in unione, se xb = 'c' cosa viene memorizzato in xa? è il numero di riferimento del carattere?
Kylex,

1
si spera che questo spieghi più in dettaglio cosa è memorizzato in xa quando si imposta xb
Kyle Cronin

1
@KyleCronin Penso di averlo capito. Nel tuo caso, hai un gruppo di tipi, sapendo che dovrai solo usarne uno ma non sai quale fino al runtime, quindi l'unione ti consente di farlo. Grazie
user12345613,

2
I sindacati @ user12345613 possono essere usati come una sorta di classe base per le strutture. Puoi emulare una gerarchia OO usando i sindacati di strutture
Morten Jensen,

1
L'ordine dei byte @Lazar in tipi multi-byte dipende dall'endianness. Suggerisco di leggere l'articolo di Wikipedia su di esso.
Kyle Cronin,

83

Ecco la risposta breve: una struttura è una struttura record: ogni elemento nella struttura assegna nuovo spazio. Quindi, una struttura simile

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

alloca almeno i (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double))byte in memoria per ogni istanza. ("Almeno" perché i vincoli di allineamento dell'architettura possono forzare il compilatore a riempire la struttura.)

D'altro canto,

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

alloca un pezzo di memoria e gli dà quattro alias. Quindi sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)), sempre con la possibilità di qualche aggiunta per gli allineamenti.


53

Esiste un buon esempio per dare la differenza tra una "struttura" e una "unione"?

Un protocollo di comunicazione immaginario

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

In questo protocollo immaginario, è stato stabilito che, in base al "tipo di messaggio", la seguente posizione nell'intestazione sarà un numero di richiesta o un codice di quattro caratteri, ma non entrambi. In breve, i sindacati consentono alla stessa posizione di archiviazione di rappresentare più di un tipo di dati, dove è garantito che si desidera archiviare solo uno dei tipi di dati alla volta.

I sindacati sono in gran parte un dettaglio di basso livello basato sull'eredità di C come linguaggio di programmazione del sistema, in cui le posizioni di memorizzazione "sovrapposte" vengono talvolta utilizzate in questo modo. A volte è possibile utilizzare i sindacati per risparmiare memoria in cui si dispone di una struttura di dati in cui verrà salvato solo uno dei diversi tipi alla volta.

In generale, il sistema operativo non si preoccupa o non conosce strutture e sindacati: entrambi sono semplicemente blocchi di memoria. Una struttura è un blocco di memoria che memorizza diversi oggetti dati, in cui tali oggetti non si sovrappongono. Un'unione è un blocco di memoria che archivia diversi oggetti dati, ma ha solo l'archiviazione per il più grande di questi, e quindi può archiviare solo uno degli oggetti dati alla volta.


1
Sì. Questo spiega bene un caso d'uso!
Gideon,

1
supponiamo di avere un packetheader ph;come accedere al numero di richiesta? ph.request.requestnumber?
justin.m.chase il

La migliore spiegazione! Grazie.
84RR1573R,

39

Come già affermato nella domanda, la differenza principale tra unione structè che i unionmembri si sovrappongono alla memoria l'uno dell'altro in modo che la dimensione di un'unione sia quella, mentre i structmembri sono disposti uno dopo l'altro (con un'imbottitura opzionale in mezzo). Anche un'unione è abbastanza grande da contenere tutti i suoi membri e ha un allineamento che si adatta a tutti i suoi membri. Quindi diciamo che intpuò essere memorizzato solo a indirizzi di 2 byte ed è largo 2 byte, mentre long può essere memorizzato solo a indirizzi di 4 byte ed è lungo 4 byte. La seguente unione

union test {
    int a;
    long b;
}; 

potrebbe avere un sizeof di 4 e un requisito di allineamento di 4. Sia un'unione che una struttura possono avere un'imbottitura alla fine, ma non all'inizio. Scrivere su una struttura cambia solo il valore del membro scritto. Scrivere a un membro di un'unione renderà non valido il valore di tutti gli altri membri. Non puoi accedervi se non li hai mai scritti prima, altrimenti il ​​comportamento non è definito. GCC fornisce un'estensione che puoi effettivamente leggere dai membri di un sindacato, anche se non hai scritto loro di recente. Per un sistema operativo, non deve importare se un programma utente scrive su un sindacato o su una struttura. Questo in realtà è solo un problema del compilatore.

Un'altra importante proprietà dell'unione e della struttura è che consentono a un puntatore di puntare su tipi di uno qualsiasi dei suoi membri . Quindi vale quanto segue:

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

some_test_pointer può puntare a int*o double*. Se lanci un indirizzo di tipo testa int*, si punterà al suo primo membro, a, in realtà. Lo stesso vale anche per un'unione. Pertanto, poiché un'unione avrà sempre il giusto allineamento, è possibile utilizzare un'unione per rendere valido il puntamento a un tipo:

union a {
    int a;
    double b;
};

Quell'unione sarà effettivamente in grado di indicare un int e un doppio:

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

è effettivamente valido, come affermato dalla norma C99:

Un oggetto deve avere il suo valore memorizzato accessibile solo da un'espressione lvalue che ha uno dei seguenti tipi:

  • un tipo compatibile con il tipo effettivo dell'oggetto
  • ...
  • un tipo di aggregato o unione che include uno dei tipi di cui sopra tra i suoi membri

Il compilatore non ottimizzerà il v->a = 10;come potrebbe influenzare il valore di *some_int_pointer(e la funzione restituirà 10invece di 5).


18

A unionè utile in un paio di scenari. unionpuò essere uno strumento per manipolazioni di livello molto basso come scrivere driver di dispositivo per un kernel.

Un esempio di ciò è sezionare un floatnumero usando uniondi a structcon bitfield e a float. Salvo un numero in float, e in seguito posso accedere a particolari parti del floattramite struct. L'esempio mostra come unionutilizzare angolazioni diverse per guardare i dati.

#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;
}

Dai un'occhiata alla singola descrizione di precisione su Wikipedia. Ho usato l'esempio e il numero magico 0.15625 da lì.


unionpuò anche essere utilizzato per implementare un tipo di dati algebrico con più alternative. Ne ho trovato un esempio nel libro "Real World Haskell" di O'Sullivan, Stewart e Goerzen. Dai un'occhiata nella sezione Unione discriminata .

Saluti!


11

" unione " e " struct " sono costrutti del linguaggio C. Parlare di una differenza di "livello OS" tra loro è inappropriato, poiché è il compilatore che produce un codice diverso se si utilizza una o un'altra parola chiave.


11

Non tecnicamente parlando significa:

Presupposto: sedia = blocco di memoria, persone = variabile

Struttura : se ci sono 3 persone possono sedersi su una sedia delle loro dimensioni di conseguenza.

Unione : se ci sono 3 persone una sola sedia sarà lì per sedersi, tutti devono usare la stessa sedia quando vogliono sedersi.

Tecnicamente parlando significa:

Il programma sotto menzionato offre un tuffo profondo nella struttura e nell'unione insieme.

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

Dimensione MAIN_STRUCT totale = sizeof (UINT64) per bufferaddr + sizeof (UNIT32) per unione + 32 bit per riempimento (dipende dall'architettura del processore) = 128 bit. Per struttura tutti i membri ottengono il blocco di memoria contiguo.

L'unione ottiene un blocco di memoria del membro di dimensione massima (qui i suoi 32 bit). All'interno dell'unione si trova un'altra struttura (INNER_STRUCT) i suoi membri ottengono un blocco di memoria di dimensioni totali 32 bit (16 + 8 + 8). In unione è possibile accedere al membro INNER_STRUCT (32 bit) o ai dati (32 bit).


Ottima spiegazione Saluti!
Prem

11

Sì, la differenza principale tra struct e union è la stessa che hai affermato. Struct utilizza tutta la memoria dei suoi membri e union utilizza lo spazio di memoria dei membri più grandi.

Ma tutta la differenza sta nella necessità di utilizzo della memoria. Il miglior utilizzo dell'unione può essere visto nei processi di unix in cui utilizziamo i segnali. come un processo può agire su un solo segnale alla volta. Quindi la dichiarazione generale sarà:

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

In questo caso, il processo utilizza solo la memoria più alta di tutti i segnali. ma se usi struct in questo caso, l'utilizzo della memoria sarà la somma di tutti i segnali. Fa molta differenza.

Per riassumere, l'Unione dovrebbe essere selezionata se si sa che si accede a uno qualsiasi dei membri alla volta.


10

Ce l'hai, tutto qui. Ma allora, in sostanza, qual è il punto dei sindacati?

È possibile inserire nella stessa posizione contenuti di tipi diversi. Devi conoscere il tipo di cosa hai memorizzato nell'unione (così spesso lo metti in a structcon un tag di tipo ...).

Perché questo è importante? Non proprio per guadagni di spazio. Sì, puoi guadagnare qualche bit o fare un po 'di riempimento, ma non è più questo il punto principale.

È per la sicurezza dei tipi, ti consente di fare una sorta di "digitazione dinamica": il compilatore sa che i tuoi contenuti possono avere significati diversi e il significato preciso di come la tua interpretazione dipende da te in fase di esecuzione. Se hai un puntatore che può puntare a tipi diversi, DEVI usare un'unione, altrimenti il ​​tuo codice potrebbe essere errato a causa di problemi di aliasing (il compilatore dice a se stesso "oh, solo questo puntatore può puntare a questo tipo, quindi posso ottimizzare fuori quegli accessi ... ", e possono succedere cose brutte).


9

Una struttura assegna la dimensione totale di tutti gli elementi in essa contenuti.

Un'unione alloca solo la quantità di memoria richiesta dal suo membro più grande.


2
Potresti anche aggiungere che i membri del sindacato si "sovrappongono" a vicenda in quanto iniziano tutti dall'indirizzo iniziale della "struttura" sindacale assegnata.
Jim Buck,

4

qual'è la differenza tra struttura e unione?

La risposta abbreviata è: la deferenza è nell'allocazione della memoria. Spiegazione: Nella struttura, verrà creato spazio di memoria per tutti i membri all'interno della struttura. Nell'unione lo spazio di memoria verrà creato solo per un membro che necessita di uno spazio di memoria maggiore. Considera il seguente codice:

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

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

Qui ci sono due membri all'interno di struct e union: int e long int. Lo spazio di memoria per int è: 4 byte e lo spazio di memoria per int lungo è: 8 nel sistema operativo a 32 bit.

Quindi per struct 4 + 8 = 12 byte verranno creati mentre 8 byte verranno creati per l'unione

Esempio di codice:

#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;
}

Rif: http://www.codingpractise.com/home/c-programming/structure-and-union/


3

Gli usi dei sindacati sindacali sono usati frequentemente quando sono necessarie conversazioni di tipo specializzato. Per avere un'idea dell'utilità dell'unione. La libreria standard c / c non definisce alcuna funzione specificatamente progettata per scrivere numeri interi brevi in ​​un file. L'uso di fwrite () comporta un sovraccarico eccessivo per operazioni semplici. Tuttavia, utilizzando un'unione è possibile creare facilmente una funzione che scrive un file binario di un numero intero breve in un byte alla volta. Presumo che numeri interi corti siano lunghi 2 byte

L'ESEMPIO:

#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);
}    

anche se putw () l'ho chiamato con un numero intero breve, era possibile usare putc () e fwrite (). Ma volevo mostrare un esempio per domare come utilizzare un'unione


3

la struttura è una raccolta di diversi tipi di dati in cui possono risiedere diversi tipi di dati e ognuno ottiene il proprio blocco di memoria

di solito abbiamo usato l'unione quando siamo sicuri che solo una delle variabili verrà utilizzata contemporaneamente e si desidera un utilizzo completo della memoria attuale perché ottiene solo un blocco di memoria che è uguale al tipo più grande.

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

memoria totale che ottiene => 5 byte

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

memoria totale che ottiene = 4 byte


2

I sindacati sono utili durante la scrittura di una funzione di ordinamento dei byte fornita di seguito. Non è possibile con le strutture.

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

Un'Unione è diversa da una struttura in quanto un'Unione si ripete sulle altre: ridefinisce la stessa memoria mentre la struttura definisce una dopo l'altra senza sovrapposizioni o ridefinizioni.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.