Unterschied zwischen einer Struktur und einer Union

411

Gibt es ein gutes Beispiel, um den Unterschied zwischen a structund a zu geben union? Grundsätzlich weiß ich, dass structder gesamte Speicher seines Mitglieds und unionder größte Speicherplatz des Mitglieds verwendet wird. Gibt es einen anderen Unterschied auf Betriebssystemebene?

gagneet
quelle

Antworten:

677

Bei einer Union sollten Sie nur eines der Elemente verwenden, da sie alle an derselben Stelle gespeichert sind. Dies ist nützlich, wenn Sie etwas speichern möchten, das einer von mehreren Typen sein kann. Eine Struktur hat andererseits einen separaten Speicherort für jedes ihrer Elemente und sie können alle gleichzeitig verwendet werden.

Um ein konkretes Beispiel für ihre Verwendung zu geben, habe ich vor einiger Zeit an einem Schema-Interpreter gearbeitet und die Schema-Datentypen im Wesentlichen auf die C-Datentypen gelegt. Dies beinhaltete das Speichern einer Aufzählung in einer Struktur, die den Werttyp angibt, und einer Vereinigung zum Speichern dieses Werts.

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

Bearbeiten: Wenn Sie sich fragen, durch welche Einstellung von xb auf 'c' der Wert von xa geändert wird, ist er technisch gesehen undefiniert. Auf den meisten modernen Maschinen ist ein Zeichen 1 Byte und ein Int 4 Byte. Wenn Sie also xb den Wert 'c' geben, erhält auch das erste Byte von xa denselben Wert:

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

druckt

99, 99

Warum sind die beiden Werte gleich? Da die letzten 3 Bytes von int 3 alle Null sind, wird es auch als 99 gelesen. Wenn wir für xa eine größere Zahl eingeben, werden Sie feststellen, dass dies nicht immer der Fall ist:

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

druckt

387427, 99

Um die tatsächlichen Speicherwerte genauer zu betrachten, setzen wir die Werte in hexadezimaler Form und drucken sie aus:

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

druckt

deadbe22, 22

Sie können deutlich sehen, wo der 0x22 den 0xEF überschrieben hat.

ABER

In C ist die Reihenfolge der Bytes in einem int nicht definiert. Dieses Programm hat die 0xEF mit 0x22 auf meinem Mac überschrieben, aber es gibt andere Plattformen, auf denen es stattdessen die 0xDE überschreibt, da die Reihenfolge der Bytes, aus denen die int besteht, umgekehrt wurde. Daher sollten Sie sich beim Schreiben eines Programms niemals auf das Verhalten beim Überschreiben bestimmter Daten in einer Union verlassen, da diese nicht portierbar sind.

Weitere Informationen zur Reihenfolge der Bytes finden Sie unter endianness .

Kyle Cronin
quelle
1
Wenn in diesem Beispiel in Union xb = 'c' ist, was wird in xa gespeichert? ist es die Referenznummer des Zeichens?
Kylex
1
hoffentlich erklärt das genauer, was in xa gespeichert ist, wenn Sie xb einstellen
Kyle Cronin
1
@ KyleCronin Ich glaube ich verstehe. In Ihrem Fall haben Sie eine Gruppe von Typen, die wissen, dass Sie nur einen verwenden müssen, aber Sie wissen erst zur Laufzeit, welcher - also können Sie dies mit der Union tun. Danke
user12345613
2
@ user12345613 Gewerkschaften können als eine Art Basisklasse für Strukturen verwendet werden. Sie können eine OO-Hierarchie mit Gewerkschaften von Strukturen emulieren
Morten Jensen
1
Die @ Lazar-Byte-Reihenfolge in Multi-Byte-Typen hängt von der Endianness ab. Ich schlage vor, den Wikipedia-Artikel darüber zu lesen.
Kyle Cronin
83

Hier ist die kurze Antwort: Eine Struktur ist eine Datensatzstruktur: Jedes Element in der Struktur weist neuen Speicherplatz zu. Also eine Struktur wie

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

weist (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double))jeder Instanz mindestens Bytes im Speicher zu. ("Zumindest", da Einschränkungen bei der Architekturausrichtung den Compiler zwingen können, die Struktur aufzufüllen.)

Auf der anderen Seite,

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

weist einen Teil des Speichers zu und gibt ihm vier Aliase. Also sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double))wieder mit der Möglichkeit einer Hinzufügung für Ausrichtungen.

Charlie Martin
quelle
53

Gibt es ein gutes Beispiel, um den Unterschied zwischen einer 'Struktur' und einer 'Union' zu machen?

Ein imaginäres Kommunikationsprotokoll

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

In diesem imaginären Protokoll wurde spezifiziert, dass basierend auf dem "Nachrichtentyp" die folgende Position im Header entweder eine Anforderungsnummer oder ein vierstelliger Code ist, aber nicht beide. Kurz gesagt, Gewerkschaften erlauben, dass derselbe Speicherort mehr als einen Datentyp darstellt, wobei garantiert ist, dass Sie immer nur einen der Datentypen gleichzeitig speichern möchten.

Gewerkschaften sind größtenteils ein Detail auf niedriger Ebene, das auf dem Erbe von C als Systemprogrammiersprache basiert, wobei manchmal "überlappende" Speicherorte auf diese Weise verwendet werden. Sie können manchmal Gewerkschaften verwenden, um Speicher zu speichern, wenn Sie eine Datenstruktur haben, in der nur einer von mehreren Typen gleichzeitig gespeichert wird.

Im Allgemeinen kümmert sich das Betriebssystem nicht um Strukturen und Gewerkschaften - beide sind einfach Speicherblöcke. Eine Struktur ist ein Speicherblock, in dem mehrere Datenobjekte gespeichert sind, wobei sich diese Objekte nicht überlappen. Eine Union ist ein Speicherblock, in dem mehrere Datenobjekte gespeichert sind, für den jedoch nur das größte gespeichert ist. Daher kann jeweils nur eines der Datenobjekte gespeichert werden.

Cygil
quelle
1
Ja. Dies erklärt einen Anwendungsfall gut!
Gideon
1
Angenommen, Sie haben eine packetheader ph;Wie greifen Sie auf die Anforderungsnummer zu? ph.request.requestnumber?
justin.m.chase
Beste Erklärung! Vielen Dank.
84RR1573R
39

Wie Sie bereits in Ihrer Frage festgestellt haben, besteht der Hauptunterschied zwischen unionund structdarin, dass unionMitglieder den Speicher voneinander überlagern, sodass die Größe einer Gewerkschaft diejenige ist, während die structMitglieder nacheinander angeordnet werden (mit optionaler Auffüllung dazwischen). Außerdem ist eine Gewerkschaft groß genug, um alle ihre Mitglieder aufzunehmen und eine Ausrichtung zu haben, die allen ihren Mitgliedern entspricht. Nehmen wir also an, es intkann nur an 2-Byte-Adressen gespeichert werden und ist 2 Byte breit, und long kann nur an 4-Byte-Adressen gespeichert werden und ist 4 Byte lang. Die folgende Vereinigung

union test {
    int a;
    long b;
}; 

könnte eine sizeofvon 4 und eine Ausrichtungsanforderung von 4 haben. Sowohl eine Vereinigung als auch eine Struktur können am Ende, aber nicht am Anfang, eine Auffüllung haben. Das Schreiben in eine Struktur ändert nur den Wert des Mitglieds, in das geschrieben wurde. Wenn Sie an ein Gewerkschaftsmitglied schreiben, wird der Wert aller anderen Mitglieder ungültig. Sie können nicht auf sie zugreifen, wenn Sie noch nicht darauf geschrieben haben. Andernfalls ist das Verhalten undefiniert. GCC bietet eine Erweiterung, die Sie tatsächlich von Gewerkschaftsmitgliedern lesen können, obwohl Sie ihnen zuletzt nicht geschrieben haben. Für ein Betriebssystem muss es keine Rolle spielen, ob ein Benutzerprogramm in eine Union oder in eine Struktur schreibt. Dies ist eigentlich nur ein Problem des Compilers.

Eine weitere wichtige Eigenschaft von union und struct ist, dass ein Zeiger auf sie auf Typen eines ihrer Mitglieder verweisen kann . Folgendes gilt also:

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

some_test_pointer kann auf int*oder zeigen double*. Wenn Sie eine Adresse vom Typ testin int*umwandeln, verweist sie atatsächlich auf das erste Mitglied . Gleiches gilt auch für eine Gewerkschaft. Da eine Union immer die richtige Ausrichtung hat, können Sie eine Union verwenden, um das Zeigen auf einen Typ gültig zu machen:

union a {
    int a;
    double b;
};

Diese Vereinigung wird tatsächlich in der Lage sein, auf ein int und ein double zu verweisen:

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

ist tatsächlich gültig, wie im C99-Standard angegeben:

Auf einen gespeicherten Wert eines Objekts darf nur über einen lvalue-Ausdruck zugegriffen werden, der einen der folgenden Typen hat:

  • Ein Typ, der mit dem effektiven Typ des Objekts kompatibel ist
  • ...
  • ein Aggregat- oder Gewerkschaftstyp, der einen der oben genannten Typen unter seinen Mitgliedern enthält

Der Compiler optimiert das nicht, v->a = 10;da es den Wert von beeinflussen könnte *some_int_pointer(und die Funktion wird 10stattdessen zurückgeben 5).

Johannes Schaub - litb
quelle
18

A unionist in einigen Szenarien nützlich. unionkann ein Werkzeug für Manipulationen auf sehr niedriger Ebene sein, wie das Schreiben von Gerätetreibern für einen Kernel.

Ein Beispiel dafür ist das Zerlegen einer floatZahl unter Verwendung unionvon a structmit Bitfeldern und a float. Ich spare eine Zahl in der float, und später kann ich bestimmte Teile der Zugriff floatdurch das struct. Das Beispiel zeigt, wie unionunterschiedliche Blickwinkel zum Betrachten von Daten verwendet werden.

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

Schauen Sie sich die Beschreibung mit einfacher Genauigkeit auf Wikipedia an. Ich habe das Beispiel und die magische Zahl 0.15625 von dort verwendet.


unionkann auch verwendet werden, um einen algebraischen Datentyp zu implementieren, der mehrere Alternativen aufweist. Ein Beispiel dafür fand ich im Buch "Real World Haskell" von O'Sullivan, Stewart und Goerzen. Überprüfen Sie es im Abschnitt Die diskriminierte Vereinigung .

Prost!

Krzysztof Voss
quelle
11

" union " und " struct " sind Konstrukte der C-Sprache. Es ist unangemessen, von einem Unterschied auf "Betriebssystemebene" zu sprechen, da der Compiler unterschiedlichen Code erzeugt, wenn Sie das eine oder andere Schlüsselwort verwenden.

Gabriele D'Antona
quelle
11

Nicht technisch gesehen bedeutet:

Annahme: Stuhl = Speicherblock, Personen = Variable

Struktur : Bei 3 Personen können sie entsprechend auf einem Stuhl ihrer Größe sitzen.

Union : Wenn es 3 Personen gibt, nur eine anwesend sind, wird Stuhl zum Sitzen da sein. Alle müssen denselben Stuhl verwenden, wenn sie sitzen möchten.

Technisch bedeutet:

Das unten erwähnte Programm gibt einen tiefen Einblick in Struktur und Vereinigung.

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

Gesamtgröße MAIN_STRUCT = sizeof (UINT64) für bufferaddr + sizeof (UNIT32) für union + 32 bit für padding (abhängig von der Prozessorarchitektur) = 128 Bit. Für die Struktur erhalten alle Mitglieder den Speicherblock zusammenhängend.

Union erhält einen Speicherblock des Mitglieds mit maximaler Größe (hier 32 Bit). Innerhalb der Union liegt eine weitere Struktur (INNER_STRUCT), deren Mitglieder einen Speicherblock mit einer Gesamtgröße von 32 Bit (16 + 8 + 8) erhalten. In Union kann entweder auf das Mitglied INNER_STRUCT (32 Bit) oder auf Daten (32 Bit) zugegriffen werden.

skanda93
quelle
Tolle Erklärung. Prost!
Prem
11

Ja, der Hauptunterschied zwischen Struktur und Vereinigung ist der gleiche wie von Ihnen angegeben. Struct verwendet den gesamten Speicher seines Mitglieds und Union den größten Speicherplatz für Mitglieder.

Der Unterschied liegt jedoch in der Nutzung des Speichers. Die beste Verwendung der Union zeigt sich in den Prozessen von Unix, in denen wir Signale verwenden. wie ein Prozess kann immer nur auf ein Signal gleichzeitig einwirken. Die allgemeine Erklärung lautet also:

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

In diesem Fall verwendet der Prozess nur den höchsten Speicher aller Signale. Wenn Sie in diesem Fall struct verwenden, ist die Speichernutzung die Summe aller Signale. Macht einen großen Unterschied.

Zusammenfassend sollte Union ausgewählt werden, wenn Sie wissen, dass Sie gleichzeitig auf eines der Mitglieder zugreifen.

Ravi Kanth
quelle
10

Du hast es, das ist alles. Aber was ist der Sinn der Gewerkschaften?

Sie können denselben Standortinhalt verschiedener Typen eingeben. Sie müssen den Typ dessen kennen, was Sie in der Union gespeichert haben (so oft setzen Sie ihn in ein structmit einem Typ-Tag ein ...).

Warum ist das wichtig? Nicht wirklich für Platzgewinne. Ja, Sie können ein paar Bits gewinnen oder etwas auffüllen, aber das ist nicht mehr der Hauptpunkt.

Es dient der Typensicherheit und ermöglicht es Ihnen, eine Art "dynamisches Tippen" durchzuführen: Der Compiler weiß, dass Ihr Inhalt unterschiedliche Bedeutungen haben kann und die genaue Bedeutung, wie Ihre Interpretation zur Laufzeit bei Ihnen liegt. Wenn Sie einen Zeiger haben, der auf verschiedene Typen verweisen kann, MÜSSEN Sie eine Union verwenden, da sonst der Code aufgrund von Aliasing-Problemen möglicherweise falsch ist (der Compiler sagt sich: "Oh, nur dieser Zeiger kann auf diesen Typ verweisen, damit ich ihn optimieren kann aus diesen Zugriffen ... ", und schlimme Dinge können passieren).

Piotr Lesnicki
quelle
9

Eine Struktur weist die Gesamtgröße aller darin enthaltenen Elemente zu.

Eine Gewerkschaft weist nur so viel Speicher zu, wie ihr größtes Mitglied benötigt.

CMS
quelle
2
Sie können auch hinzufügen, dass sich Gewerkschaftsmitglieder gegenseitig "überlagern", indem sie alle an der Anfangsadresse der zugewiesenen "Gewerkschaftsstruktur" beginnen.
Jim Buck
4

Was ist der Unterschied zwischen Struktur und Vereinigung?

Die Antwort lautet: Die Rücksichtnahme liegt in der Speicherzuweisung. Erläuterung: In der Struktur wird Speicherplatz für alle Mitglieder innerhalb der Struktur erstellt. In Union wird Speicherplatz nur für ein Mitglied erstellt, das den größten Speicherplatz benötigt. Betrachten Sie den folgenden Code:

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

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

Hier gibt es zwei Mitglieder innerhalb von struct und union: int und long int. Der Speicherplatz für int beträgt: 4 Byte und der Speicherplatz für long int beträgt: 8 im 32-Bit-Betriebssystem.

Für Struktur 4 + 8 = werden 12 Bytes erstellt, während 8 Bytes für die Vereinigung erstellt werden

Codebeispiel:

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

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

Abdus Sattar Bhuiyan
quelle
3

Die Verwendung von Gewerkschaftsverbänden wird häufig verwendet, wenn spezielle Typkonversationen erforderlich sind. Um sich ein Bild von der Nützlichkeit der Vereinigung zu machen. Die c / c-Standardbibliothek definiert keine Funktion, die speziell zum Schreiben kurzer Ganzzahlen in eine Datei entwickelt wurde. Die Verwendung von fwrite () verursacht einen übermäßigen Overhead für eine einfache Bedienung. Mit einer Union können Sie jedoch leicht eine Funktion erstellen, die binär eine kurze Ganzzahl byteweise in eine Datei schreibt. Ich gehe davon aus, dass kurze Ganzzahlen 2 Byte lang sind

DAS BEISPIEL:

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

Obwohl putw () mit einer kurzen Ganzzahl aufgerufen wurde, war es möglich, putc () und fwrite () zu verwenden. Aber ich wollte ein Beispiel zeigen, um zu dominieren, wie eine Gewerkschaft verwendet werden kann

Ahmed
quelle
3

Struktur ist die Sammlung verschiedener Datentypen, in denen sich verschiedene Datentypen befinden können und jeder seinen eigenen Speicherblock erhält

Wir haben normalerweise union verwendet, wenn wir sicher sind, dass nur eine der Variablen gleichzeitig verwendet wird und Sie den aktuellen Speicher vollständig nutzen möchten, da nur ein Speicherblock vorhanden ist, der dem größten Typ entspricht.

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

Gesamtspeicher erhält es => 5 Byte

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

Gesamtspeicher = 4 Byte

Anurag Bhakuni
quelle
2

Gewerkschaften sind praktisch, wenn Sie eine Byte-Bestellfunktion schreiben, die unten angegeben ist. Mit Strukturen ist das nicht möglich.

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

Eine Union unterscheidet sich von einer Struktur, da sich eine Union gegenüber den anderen wiederholt: Sie definiert denselben Speicher neu, während die Struktur nacheinander ohne Überlappungen oder Neudefinitionen definiert.

Dennis Ng
quelle