Warum brauchen wir C Unions?

196

Wann sollten Gewerkschaften verwendet werden? Warum brauchen wir sie?

    
timrau 31.10.2008, 03:51
quelle

18 Antworten

220

Um die Binärdarstellungen von Ganzzahlen und Gleitkommazahlen zu konvertieren, werden oft Verknüpfungen verwendet:

%Vor%

Obwohl dies technisch nicht definiertes Verhalten gemäß dem C-Standard ist (Sie sollen nur das Feld lesen, das zuletzt geschrieben wurde), wird es in praktisch jedem Compiler in wohldefinierter Weise funktionieren.

Unionen werden manchmal auch verwendet, um Pseudopolymorphie in C zu implementieren, indem man einer Struktur ein Tag gibt, das angibt, welche Art von Objekt es enthält, und dann die möglichen Typen zusammenführt:

%Vor%

Dies ermöglicht die Größe von struct S nur noch 12 Byte statt 28 zu sein.

    
Adam Rosenfield 31.10.2008 04:02
quelle
107

Verbindungen sind besonders nützlich in Embedded-Programmierung oder in Situationen, in denen direkter Zugriff auf die Hardware / Speicher benötigt wird. Hier ist ein triviales Beispiel:

%Vor%

Dann können Sie wie folgt auf die Registrierung zugreifen:

%Vor%

Endianess (Byte-Reihenfolge) und Prozessor-Architektur sind natürlich wichtig.

Ein weiteres nützliches Feature ist der Bitmodifikator:

%Vor%

Mit diesem Code können Sie direkt auf ein einzelnes Bit in der Register- / Speicheradresse zugreifen:

%Vor%     
kgiannakakis 31.10.2008 07:13
quelle
49

Low-Level-Systemprogrammierung ist ein sinnvolles Beispiel.

IIRC, ich habe Gewerkschaften verwendet, um Hardware-Register in die Komponenten-Bits aufzuteilen. Sie können also auf ein 8-Bit-Register zugreifen (so wie es war, an dem Tag, an dem ich das gemacht habe ;-)) in die Komponenten-Bits.

(Ich vergesse die genaue Syntax, aber ...) Diese Struktur würde es erlauben, auf ein Steuerregister als control_byte oder über die einzelnen Bits zuzugreifen. Es wäre wichtig sicherzustellen, dass die Bits auf die korrekten Registerbits für eine gegebene Endianz abgebildet werden.

%Vor%     
Snips 29.08.2011 09:29
quelle
29

Ich habe es in einigen Bibliotheken als Ersatz für objektorientierte Vererbung gesehen.

z. B.

%Vor%

Wenn Sie möchten, dass die Klasse "Connection" eine der oben genannten Komponenten ist, könnten Sie etwas schreiben wie:

%Vor%

Beispiel in libinfinity verwenden: Ссылка

    
bb-generation 29.08.2011 09:24
quelle
26

Unions erlauben Datenelementen, die sich gegenseitig ausschließen, denselben Speicher zu teilen. Dies ist sehr wichtig, wenn der Speicher knapper ist, beispielsweise in eingebetteten Systemen.

Im folgenden Beispiel:

%Vor%

Diese Union wird den Platz eines einzelnen Int anstelle von drei separaten int-Werten einnehmen. Wenn der Benutzer den Wert a festlegt und den Wert für b festlegt, überschreibt er den Wert von a , da beide freigegeben sind der gleiche Speicherort.

    
LeopardSkinPillBoxHat 31.10.2008 03:59
quelle
20

Viele Nutzungen. Mach einfach grep union /usr/include/* oder in ähnlichen Verzeichnissen. In den meisten Fällen ist das union in ein struct eingeschlossen und ein Mitglied der Struktur teilt mit, auf welches Element in der Union zugegriffen werden soll. Zum Beispiel checkout man elf für reale Implementierungen.

Dies ist das Grundprinzip:

%Vor%     
phoxis 29.08.2011 09:21
quelle
16

Hier ist ein Beispiel für eine Union aus meiner eigenen Codebasis (aus dem Speicher und paraphrasiert, so dass es nicht genau sein kann). Es wurde verwendet, um Sprachelemente in einem von mir erstellten Interpreter zu speichern. Zum Beispiel der folgende Code:

%Vor%

besteht aus den folgenden Sprachelementen:

  • Symbol [Satz]
  • Variable [a]
  • Symbol [to]
  • Variable [b]
  • Symbol [mal]
  • Konstante [7]
  • Symbol [.]

Sprachelemente wurden als ' #define ' Werte definiert:

%Vor%

und die folgende Struktur wurde verwendet, um jedes Element zu speichern:

%Vor%

dann war die Größe jedes Elements die Größe der maximalen Union (4 Byte für den Typ und 4 Byte für die Union, obwohl dies typische Werte sind, die tatsächlichen Größen stehen für die Implementierung) .

Um ein "set" -Element zu erstellen, verwenden Sie:

%Vor%

Um ein "variable [b]" Element zu erstellen, würden Sie verwenden:

%Vor%

Um ein "constant [7]" Element zu erstellen, würden Sie verwenden:

%Vor%

und Sie könnten es einfach erweitern, um Floats ( float flt ) oder rationals ( struct ratnl {int num; int denom;} ) und andere Typen einzuschließen.

Die Grundvoraussetzung ist, dass die str und val im Speicher nicht zusammenhängend sind, sie überlappen sich tatsächlich, also ist es eine Möglichkeit, eine andere Ansicht auf den gleichen Speicherblock zu erhalten, wie hier dargestellt am Speicherort 0x1010 und Integer und Zeiger sind beide 4 Bytes:

%Vor%

Wenn es nur in einer Struktur wäre, würde es so aussehen:

%Vor%     
paxdiablo 31.10.2008 06:09
quelle
6

Ich würde sagen, dass es die Wiederverwendung von Speicher vereinfacht, der auf verschiedene Arten verwendet werden kann, d. h. Speicher zu sparen. Z.B. Sie möchten eine "variante" -Struktur erstellen, die sowohl eine kurze Zeichenfolge als auch eine Zahl speichern kann:

%Vor%

In einem 32-Bit-System würde dies dazu führen, dass mindestens 96 Bits oder 12 Bytes für jede Instanz von variant verwendet werden.

Mit einer Union können Sie die Größe auf 64 oder 8 Byte reduzieren:

%Vor%

Sie können sogar noch mehr speichern, wenn Sie mehr verschiedene Variablentypen hinzufügen möchten. Es könnte richtig sein, dass Sie ähnliche Dinge tun können, indem Sie einen void-Zeiger erzeugen - aber die Vereinigung macht es viel zugänglicher als gut als Typ sicher. Solche Einsparungen klingen nicht massiv, aber Sie sparen ein Drittel des Speichers für alle Instanzen dieser Struktur.

    
Mario 29.08.2011 09:21
quelle
5

Es ist schwierig, an eine bestimmte Gelegenheit zu denken, wenn Sie diese Art von flexibler Struktur benötigen, vielleicht in einem Nachrichtenprotokoll, in dem Sie verschiedene Größen von Nachrichten senden würden, aber selbst dann gibt es wahrscheinlich bessere und programmiererfreundlichere Alternativen.

Gewerkschaften sind ein bisschen wie Variantentypen in anderen Sprachen - sie können immer nur eine Sache auf einmal halten, aber diese Sache könnte ein Int, ein Float usw. sein, je nachdem, wie Sie es deklarieren.

Zum Beispiel:

%Vor%

MyUnion wird nur einen int ODER einen Gleitkommawert enthalten, je nachdem, was Sie zuletzt festgelegt haben . Also mach das:

%Vor%

u hält jetzt einen int gleich 10;

%Vor%

u enthält jetzt einen Gleitkommawert gleich 1.0. Es enthält nicht mehr einen int. Offensichtlich jetzt, wenn Sie versuchen und printf ("MyInt =% d", u.MyInt); dann werden Sie wahrscheinlich einen Fehler bekommen, obwohl ich mir des spezifischen Verhaltens nicht sicher bin.

Die Größe der Union wird durch die Größe ihres größten Feldes bestimmt, in diesem Fall des Floats.

    
Xiaofu 31.10.2008 04:16
quelle
4

Unions werden verwendet, wenn Sie Strukturen modellieren möchten, die durch Hardware, Geräte oder Netzwerkprotokolle definiert sind, oder wenn Sie eine große Anzahl von Objekten erstellen und Speicherplatz sparen möchten. Sie brauchen sie wirklich nicht in 95% der Zeit, bleiben Sie jedoch bei leicht zu debugendem Code.

    
Paul Betts 31.10.2008 03:59
quelle
3

Viele dieser Antworten betreffen das Gießen von einem Typ zum anderen. Ich bekomme von Vereinigungen mit den gleichen Typen am meisten Gebrauch nur mehr von ihnen (zB beim Parsen eines seriellen Datenstroms). Sie ermöglichen die Parsing / Konstruktion eines gerahmten -Pakets trivial zu werden.

%Vor%

Bearbeiten Der Kommentar zu Endianness und Struct-Padding sind gültige und große Anliegen. Ich habe diesen Codecode fast ausschließlich in eingebetteter Software verwendet, von denen die meisten die Kontrolle über beide Enden der Pipe hatten.

    
Adam Lewis 27.09.2013 16:34
quelle
1

Gewerkschaften sind großartig. Eine kluge Verwendung von Gewerkschaften, die ich gesehen habe, besteht darin, sie bei der Definition eines Ereignisses zu verwenden. Zum Beispiel könnten Sie entscheiden, dass ein Ereignis 32 Bits ist.

Nun, innerhalb dieser 32 Bits könnten Sie die ersten 8 Bits wie für eine Kennung des Absenders des Ereignisses festlegen ... Manchmal behandeln Sie das Ereignis als Ganzes, manchmal sezieren Sie es und vergleichen seine Komponenten . Gewerkschaften geben Ihnen die Flexibilität, beides zu tun.

%Vor%     
dicroce 31.10.2008 04:36
quelle
1

Was ist mit VARIANT ? das wird in COM-Schnittstellen verwendet? Es hat zwei Felder - "type" und eine Union, die einen tatsächlichen Wert enthält, der abhängig vom Feld "type" behandelt wird.

    
sharptooth 29.08.2011 09:16
quelle
1

In der Schule habe ich solche Gewerkschaften benutzt:

%Vor%

Ich habe es verwendet, um Farben leichter zu handhaben, anstatt & gt; & gt; und & lt; & lt; Operatoren musste ich einfach durch den verschiedenen Index meiner Char-Array gehen.

    
Zoneur 29.08.2011 09:23
quelle
1

Ich habe Union verwendet, als ich für eingebettete Geräte programmiert habe. Ich habe C int, das ist 16 Bit lang. Und ich muss die höheren 8 Bits und die unteren 8 Bits abrufen, wenn ich vom EEPROM lesen / speichern muss. Also habe ich diesen Weg gewählt:

%Vor%

Es erfordert keine Verschiebung, so dass der Code leichter zu lesen ist.

Auf der anderen Seite sah ich einen alten C ++ - Code, der die Union für den Stl-Allokator verwendete. Wenn Sie interessiert sind, können Sie den sgi stl Quellcode lesen. Hier ist ein Teil davon:

%Vor%     
Mu Qiao 29.08.2011 09:20
quelle
1
  • Eine Datei mit verschiedenen Datensatztypen.
  • Eine Netzwerkschnittstelle, die verschiedene Anfragetypen enthält.

Werfen Sie einen Blick darauf: X .25 Pufferbefehlsverarbeitung

Einer der vielen möglichen X.25-Befehle wird in einem Puffer empfangen und durch Verwendung einer UNION aller möglichen Strukturen an Ort und Stelle gehandhabt.

    
James Anderson 29.08.2011 09:20
quelle
1

In früheren Versionen von C würden alle Strukturdeklarationen einen gemeinsamen Satz von Feldern teilen. Gegeben:

%Vor%

Ein Compiler würde im Wesentlichen eine Tabelle der Größe der Strukturen (und möglicherweise Alignments) und eine separate Tabelle mit den Namen, Typen und Offsets der Strukturelemente erzeugen. Der Compiler hat nicht verfolgt, welche Mitglieder zu welchen Strukturen gehören, und würde zwei Strukturen erlauben, nur dann ein Mitglied mit dem gleichen Namen zu haben, wenn Typ und Offset übereinstimmen (wie bei q von struct x und struct y ). Wenn p ein Zeiger auf irgendeinen Strukturtyp wäre, würde p- & gt; q den Versatz von "q" zu Zeiger p addieren und ein "int" von der resultierenden Adresse holen.

Angesichts der obigen Semantik war es möglich, eine Funktion zu schreiben, die einige nützliche Operationen für verschiedene Arten von Strukturen austauschbar ausführen könnte, vorausgesetzt, dass alle von der Funktion verwendeten Felder mit nützlichen Feldern innerhalb der betreffenden Strukturen aufgereiht sind. Dies war ein nützliches Feature, und das Ändern von C zum Validieren von Mitgliedern, die für den Strukturzugriff gegen die Typen der fraglichen Strukturen verwendet wurden, hätte bedeutet, sie zu verlieren, ohne eine Struktur zu haben, die mehrere benannte Felder an derselben Adresse enthalten kann. Das Hinzufügen von "Union" -Typen zu C half, diese Lücke etwas zu füllen (obwohl nicht, IMHO, so wie es sein sollte).

Ein wesentlicher Teil der Fähigkeit der Gewerkschaften, diese Lücke zu schließen, war die Tatsache, dass ein Zeiger auf ein Union-Element in einen Zeiger auf eine Union mit diesem Member konvertiert werden konnte und ein Zeiger auf eine Union in einen Zeiger umgewandelt werden konnte jedes Mitglied. Der C89-Standard besagt zwar nicht ausdrücklich, dass das direkte Umwandeln eines T* in ein U* einem Zeiger auf einen Union-Typ entspricht, der sowohl T als auch U enthält, und dann auf U* , würde kein definiertes Verhalten der letzten Cast-Sequenz durch den verwendeten Union-Typ beeinflusst, und der Standard hat keine gegenteilige Semantik für eine direkte Umwandlung von T nach U angegeben. Außerdem würde in Fällen, in denen eine Funktion einen Zeiger unbekannter Herkunft erhielt, das Schreiben eines Objekts über T* , das Konvertieren von T* in U* und das anschließende Lesen des Objekts über U* dem Schreiben gleichwertig sein eine Union über Member vom Typ T und Lesen als Typ U , die in einigen Fällen standarddefiniert (z. B. beim Zugriff auf Common-Initial-Sequence-Member) und für den Rest implementation-defined (statt Undefined) wären. Während es bei Programmen selten vorkam, die CIS-Garantien mit tatsächlichen Objekten des Unionstyps auszunutzen, war es weit üblicher, die Tatsache auszunutzen, dass Zeiger auf Objekte unbekannter Herkunft sich wie Zeiger auf Gewerkschaftsmitglieder verhalten mussten und die damit verbundenen Verhaltensgarantien aufweisen.

    
supercat 20.04.2017 18:21
quelle
0

Ein einfaches und sehr nützliches Beispiel ist ....

Stellen Sie sich vor:

Sie haben uint32_t array[2] und möchten auf das 3. und 4. Byte der Byte-Kette zugreifen. Du könntest *((uint16_t*) &array[1]) machen. Aber das bricht leider die strengen Aliasing-Regeln!

Aber bekannte Compiler ermöglichen Ihnen Folgendes:

%Vor%

technisch ist dies immer noch ein Verstoß gegen die Regeln. aber alle bekannten Standards unterstützen diese Verwendung.

    
dhein 18.09.2013 15:07
quelle

Tags und Links