467 Stimmen

Unterschied zwischen einer Struktur und einem Verband

Gibt es ein gutes Beispiel, das den Unterschied zwischen einer struct und eine union ? Im Grunde weiß ich, dass struct verwendet den gesamten Speicher seines Mitglieds und union verwendet den größten Speicherplatz der Mitglieder. Gibt es weitere Unterschiede auf Betriebssystemebene?

752voto

Kyle Cronin Punkte 74993

Bei einer Vereinigung darf man nur eines der Elemente verwenden, da sie alle an derselben Stelle gespeichert sind. Dies ist nützlich, wenn man etwas speichern möchte, das von mehreren Typen sein könnte. Eine Struktur hingegen hat für jedes ihrer Elemente einen eigenen Speicherplatz, und sie können alle gleichzeitig verwendet werden.

Um ein konkretes Beispiel für ihre Verwendung zu geben, arbeitete ich vor einiger Zeit an einem Scheme-Interpreter und überlagerte im Wesentlichen die Scheme-Datentypen mit den C-Datentypen. Dazu musste ich in einer Struktur ein Enum speichern, das den Typ des Wertes angibt, und eine Union, die diesen Wert speichert.

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, was die Einstellung von x.b auf 'c' mit dem Wert von x.a zu tun hat, ist dies technisch gesehen undefiniert. Auf den meisten modernen Rechnern ist ein char 1 Byte und ein int 4 Byte groß. Wenn Sie also x.b den Wert 'c' geben, erhält auch das erste Byte von x.a den gleichen 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? Weil die letzten 3 Bytes des int 3 alle Nullen sind, so dass er auch als 99 gelesen wird. Wenn wir eine größere Zahl für x.a eingeben, werden Sie sehen, 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 einen genaueren Blick auf die tatsächlichen Speicherwerte zu werfen, können wir die Werte in Hexadezimalwerten eingeben und ausdrucken:

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 die 0x22 die 0xEF überschrieben hat.

BUT

In C ist die Reihenfolge der Bytes in einem int nicht definiert. Dieses Programm überschreibt auf meinem Mac die 0xEF mit 0x22, aber auf anderen Plattformen würde es stattdessen die 0xDE überschreiben, weil die Reihenfolge der Bytes, aus denen der int besteht, vertauscht ist. Wenn Sie ein Programm schreiben, sollten Sie sich daher nie auf das Verhalten des Überschreibens bestimmter Daten in einer Union verlassen, da es nicht portabel ist.

Weitere Informationen über die Anordnung von Bytes finden Sie unter endianness .

95voto

Charlie Martin Punkte 106684

Hier die kurze Antwort: eine struct ist eine Datensatzstruktur: jedes Element in der struct weist neuen Speicherplatz zu. Also, eine Struktur wie

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

weist mindestens (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double)) Bytes im Speicher für jede Instanz. ("Mindestens", weil der Compiler aufgrund von Ausrichtungsbeschränkungen der Architektur gezwungen sein kann, die Struktur aufzufüllen).

Andererseits,

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

weist ein Stück Speicher zu und gibt ihm vier Aliasnamen. Also sizeof(union foobarbazquux_u) max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)) wiederum mit der Möglichkeit eines Zusatzes für Angleichungen.

64voto

cygil Punkte 3514

Gibt es ein gutes Beispiel für den Unterschied zwischen einer "struct" und einer "union"?

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 festgelegt, dass je nach "Nachrichtentyp" die folgende Stelle im Header entweder eine Anforderungsnummer oder ein vierstelliger Code ist, aber nicht beides. Kurz gesagt, die Gewerkschaften ermöglichen es, dass ein und derselbe Speicherplatz für mehr als einen Datentyp steht, wobei sichergestellt ist, dass immer nur einer der Datentypen gespeichert werden soll.

Unions sind größtenteils ein Low-Level-Detail, das auf das Erbe von C als Systemprogrammiersprache zurückgeht, wo "überlappende" Speicherplätze manchmal auf diese Weise verwendet werden. Sie können Unions manchmal verwenden, um Speicher zu sparen, wenn Sie eine Datenstruktur haben, bei der nur einer von mehreren Typen gleichzeitig gespeichert wird.

Im Allgemeinen kümmert sich das Betriebssystem nicht um Structs und Unions und kennt sie nicht - für es sind beides einfach Speicherblöcke. Eine Struktur ist ein Speicherblock, der mehrere Datenobjekte speichert, wobei sich diese Objekte nicht überschneiden. Eine Union ist ein Speicherblock, der mehrere Datenobjekte speichert, aber nur Platz für das größte dieser Objekte hat und daher immer nur eines der Datenobjekte speichern kann.

39voto

Wie Sie bereits in Ihrer Frage schreiben, besteht der Hauptunterschied zwischen union y struct ist das union Mitglieder überlagern den Speicher des jeweils anderen, so dass die Größe einer Vereinigung diejenige ist, während struct Mitglieder werden nacheinander angeordnet (mit optionalen Füllungen dazwischen). Auch eine Union ist groß genug, um alle ihre Mitglieder zu enthalten und eine Ausrichtung zu haben, die zu allen ihren Mitgliedern passt. Sagen wir also int kann 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 sizeof von 4 und eine Ausrichtungsanforderung von 4. Sowohl eine union als auch eine struct können am Ende Auffüllungen haben, aber nicht an ihrem Anfang. Das Schreiben in eine Struktur ändert nur den Wert des Elements, in das geschrieben wird. Das Schreiben auf ein Mitglied einer Union macht den Wert aller anderen Mitglieder ungültig. Man kann nicht auf sie zugreifen, wenn man nicht vorher auf sie geschrieben hat, ansonsten ist das Verhalten undefiniert. Der GCC bietet als Erweiterung an, dass man von den Mitgliedern einer Union lesen kann, auch wenn man nicht zuletzt in sie geschrieben hat. Für ein Operationssystem muss es keinen Unterschied machen, ob ein Benutzerprogramm in eine Union oder in eine Struktur schreibt. Dies ist eigentlich nur eine Frage des Compilers.

Eine weitere wichtige Eigenschaft von union und struct ist, dass sie ermöglichen, dass ein Zeiger auf sie kann auf die Typen aller ihrer Mitglieder zeigen . Es gilt also das Folgende:

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

some_test_pointer kann zeigen auf int* o double* . Wenn Sie eine Adresse des Typs test a int* verweist er auf sein erstes Mitglied, a eigentlich. Das Gleiche gilt auch für eine Gewerkschaft. Da eine Union also immer die richtige Ausrichtung hat, können Sie eine Union verwenden, um einen Verweis auf einen Typ gültig zu machen:

union a {
    int a;
    double b;
};

Diese Union kann tatsächlich auf einen int und einen double verweisen:

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

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

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

  • einen Typ, der mit dem effektiven Typ des Objekts kompatibel ist
  • ...
  • ein Aggregat- oder Vereinigungstyp, der einen der oben genannten Typen zu seinen Mitgliedern zählt

Der Compiler optimiert nicht die v->a = 10; da sie sich auf den Wert von *some_int_pointer (und die Funktion gibt 10 anstelle von 5 ).

20voto

Krzysztof Voss Punkte 1652

A union ist in einer Reihe von Szenarien nützlich. union kann 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 Sezieren einer float Nummer durch Verwendung von union eines struct mit Bitfeldern und einem float . Ich speichere eine Nummer in der float und später kann ich auf bestimmte Teile der float dadurch struct . Das Beispiel zeigt, wie union wird verwendet, um die Daten aus verschiedenen Blickwinkeln zu betrachten.

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

Werfen Sie einen Blick auf einfache Genauigkeit Beschreibung auf wikipedia. Ich habe das Beispiel und die magische Zahl 0,15625 von dort übernommen.


union kann auch verwendet werden, um einen algebraischen Datentyp zu implementieren, der mehrere Alternativen hat. Ein Beispiel dafür habe ich in dem Buch "Real World Haskell" von O'Sullivan, Stewart und Goerzen gefunden. Schauen Sie es sich in der Die diskriminierte Gewerkschaft section.

Zum Wohl!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X