webentwicklung-frage-antwort-db.com.de

Wann würde jemand eine Gewerkschaft benutzen? Ist es ein Überbleibsel aus den C-Only-Tagen?

Ich habe gelernt, aber nicht wirklich Gewerkschaften. Jeder C- oder C++ - Text, den ich durchsehe, führt sie ein (manchmal beiläufig), aber sie geben nur sehr wenige praktische Beispiele an, warum oder wo sie verwendet werden sollen. Wann wären Gewerkschaften in einem modernen (oder sogar älteren) Fall nützlich? Meine einzigen zwei Vermutungen wären das Programmieren von Mikroprozessoren, wenn Sie nur über sehr begrenzten Speicherplatz verfügen oder wenn Sie eine API (oder ähnliches) entwickeln und Sie den Endbenutzer zwingen möchten, nur eine Instanz mehrerer Objekte/Typen zu verwenden einmal. Sind diese beiden Vermutungen schon nahe beieinander? 

121
Russel

Gewerkschaften werden normalerweise zusammen mit einem Diskriminator verwendet: Eine Variable, die angibt, welche Felder der Vereinigung gültig sind. Angenommen, Sie möchten einen eigenen Variant Typ erstellen:

struct my_variant_t {
    int type;
    union {
        char char_value;
        short short_value;
        int int_value;
        long long_value;
        float float_value;
        double double_value;
        void* ptr_value;
    };
};

Dann würden Sie es wie folgt verwenden:

/* construct a new float variant instance */
void init_float(struct my_variant_t* v, float initial_value) {
    v->type = VAR_FLOAT;
    v->float_value = initial_value;
}

/* Increments the value of the variant by the given int */
void inc_variant_by_int(struct my_variant_t* v, int n) {
    switch (v->type) {
    case VAR_FLOAT:
        v->float_value += n;
        break;

    case VAR_INT:
        v->int_value += n;
        break;
    ...
    }
}

Dies ist in der Tat eine ziemlich gebräuchliche Redewendung, besonders bei Visual Basic-Interna. 

Für ein reales Beispiel siehe SDLs SDL_Event union . ( aktueller Quellcode hier ). Oben in der Union befindet sich ein type-Feld, und dasselbe Feld wird in jeder SDL_ * -Ereignistruktur wiederholt. Um mit dem korrekten Ereignis umzugehen, müssen Sie den Wert des Feldes type überprüfen.

Die Vorteile sind einfach: Es gibt einen einzigen Datentyp, der alle Ereignistypen ohne unnötigen Speicher verarbeiten kann.

96
vz0

Ich finde C++ - Vereinigungen ziemlich cool. Es scheint, dass die Leute normalerweise nur an den Anwendungsfall denken, bei dem man den Wert einer Union-Instanz "in place" ändern möchte (was anscheinend nur dazu dient, Speicher zu speichern oder zweifelhafte Konvertierungen durchzuführen).

Tatsächlich können Gewerkschaften als Software-Engineering-Tool von großer Bedeutung sein. selbst wenn Sie niemals den Wert einer Union-Instanz ändern.

Anwendungsfall 1: das Chamäleon

Mit Unionen können Sie eine Reihe von beliebigen Klassen unter einer Denomination umgruppieren, was nicht mit Ähnlichkeiten mit dem Fall einer Basisklasse und ihrer abgeleiteten Klassen verbunden ist. Was sich jedoch ändert, ist das, was Sie mit einer bestimmten Unionsinstanz tun können und nicht tun können:

struct Batman;
struct BaseballBat;

union Bat
{
    Batman brucewayne;
    BaseballBat club;
};

ReturnType1 f(void)
{
    BaseballBat bb = {/* */};
    Bat b;
    b.club = bb;
    // do something with b.club
}

ReturnType2 g(Bat& b)
{
    // do something with b, but how do we know what's inside?
}

Bat returnsBat(void);
ReturnType3 h(void)
{
    Bat b = returnsBat();
    // do something with b, but how do we know what's inside?
}

Es scheint, dass der Programmierer sich der Art des Inhalts einer gegebenen Unionsinstanz sicher sein muss, wenn er sie verwenden möchte. Dies ist in der obigen Funktion f der Fall. Wenn eine Funktion jedoch eine Unionsinstanz als übergebenes Argument erhalten würde, wie dies bei g der Fall ist, würde sie nicht wissen, was sie damit tun soll. Gleiches gilt für Funktionen, die eine Union-Instanz zurückgeben, siehe h: Woher weiß der Aufrufer, was sich in ihm befindet?

Wenn eine Union-Instanz nie als Argument oder als Rückgabewert übergeben wird, muss sie ein sehr eintöniges Leben mit Erregungsspitzen haben, wenn der Programmierer seinen Inhalt ändert:

Batman bm = {/* */};
Baseball bb = {/* */};
Bat b;
b.brucewayne = bm;
// stuff
b.club = bb;

Und das ist der (un) populärste Anwendungsfall von Gewerkschaften. Ein anderer Anwendungsfall liegt vor, wenn eine Union-Instanz mit etwas über ihren Typ informiert wird.

Anwendungsfall 2: "Schön Sie kennenzulernen, ich bin object, von Class"

Angenommen, ein Programmierer hat sich dafür entschieden, eine Union-Instanz immer mit einem Typdeskriptor zu koppeln (ich überlasse es dem Leser, eine Implementierung für ein solches Objekt vorzustellen). Dies setzt den Zweck der Union selbst außer Kraft, wenn der Programmierer Speicherplatz sparen möchte und die Größe des Typdeskriptors in Bezug auf die der Union nicht zu vernachlässigen ist. Nehmen wir jedoch an, dass es entscheidend ist, dass die Unionsinstanz als Argument oder als Rückgabewert übergeben werden kann, wenn der Aufpasser oder Anrufer nicht weiß, was sich darin befindet.

Dann muss der Programmierer eine switch-Kontrollflussanweisung schreiben, um Bruce Wayne von einem Holzstab oder etwas Ähnlichem zu unterscheiden. Es ist nicht so schlimm, wenn es nur zwei Arten von Inhalten in der Union gibt, aber offensichtlich skaliert die Union nicht mehr.

Anwendungsfall 3:

Wie die Autoren von eine Empfehlung für den ISO-C++ - Standard im Jahr 2008 wieder aufgenommen haben,

Viele wichtige Problemdomänen erfordern entweder eine große Anzahl von Objekten oder einen begrenzten Speicherplatz Ressourcen. In diesen Situationen ist das Einsparen von Platz sehr wichtig, und eine Vereinigung ist oft der perfekte Weg, dies zu tun. Tatsächlich ist ein häufiger Anwendungsfall die Situation, in der eine Gewerkschaft ihr aktives Mitglied während ihrer Lebenszeit nie ändert. Es kann so konstruiert, kopiert und zerstört werden, als wäre es eine Struktur, die nur ein Mitglied enthält. Eine typische Anwendung hierfür wäre die Erstellung einer heterogenen Sammlung nicht verwandter Typen, die nicht dynamisch zugewiesen werden (vielleicht sind sie direkt in einer Karte oder Mitgliedern eines Arrays konstruiert).

Und nun ein Beispiel mit einem UML-Klassendiagramm:

many compositions for class A

Die Situation im Klartext: ein Objekt der Klasse A können haben Objekte einer beliebigen Klasse zwischen B1, ..., Bn und höchstens einem von jedem Typ mit n eine ziemlich große Zahl zu sein, sagen wir mindestens 10.

Wir möchten A-Felder nicht wie folgt hinzufügen:

private:
    B1 b1;
    .
    .
    .
    Bn bn;

weil n variieren kann (wir möchten vielleicht Bx-Klassen zum Mix hinzufügen), und weil dies zu einem Durcheinander mit Konstruktoren führen würde und A-Objekte viel Platz beanspruchen würden.

Wir könnten einen verrückten Container von void*-Zeigern verwenden, um Bx-Objekte mit Casts abzurufen, um sie abzurufen, aber das ist flüchtig und so im C-Stil ... aber noch wichtiger ist, dass uns die Lebensdauer vieler dynamisch zugewiesener Objekte übrig bleibt.

Stattdessen können Sie Folgendes tun:

union Bee
{
    B1 b1;
    .
    .
    .
    Bn bn;
};

enum BeesTypes { TYPE_B1, ..., TYPE_BN };

class A
{
private:
    std::unordered_map<int, Bee> data; // C++11, otherwise use std::map

public:
    Bee get(int); // the implementation is obvious: get from the unordered map
};

Um den Inhalt einer Union-Instanz von data abzurufen, verwenden Sie a.get(TYPE_B2).b2 und die Likes, wobei a eine Klasse A-Instanz ist.

Dies ist umso leistungsfähiger, als in C++ 11 Gewerkschaften uneingeschränkt verfügbar sind. Weitere Informationen finden Sie in dem Dokument, das mit dem obigen Artikel verknüpft ist oder diesem Artikel .

81
jrsala

Ein Beispiel ist der eingebettete Bereich, in dem jedes Bit eines Registers etwas anderes bedeuten kann. Bei einer Vereinigung einer 8-Bit-Ganzzahl und einer Struktur mit 8 separaten 1-Bit-Feldern können Sie beispielsweise ein Bit oder das gesamte Byte ändern.

35
Kevin

Herb Sutter schrieb in GOTW vor etwa sechs Jahren mit Hervorhebung hinzugefügt:

"Aber denken Sie nicht, dass Gewerkschaften nur ein Überbleibsel aus früheren Zeiten sind. Vereinigungen sind möglicherweise am nützlichsten, um Speicherplatz zu sparen, indem sie zulassen, dass sich Daten überschneiden, und dies ist in C++ noch wünschenswert und in der heutigen Zeit Moderne Welt: Zum Beispiel verwenden einige der fortschrittlichsten C++ Standard-Bibliotheksimplementierungen der Welt jetzt genau diese Technik für die Implementierung der "kleinen String-Optimierung", einer großen Optimierungsalternative, die den Speicher wiederverwendet Innerhalb eines String-Objekts selbst: Bei großen Strings speichert der Platz innerhalb des String-Objekts den üblichen Zeiger auf den dynamisch zugewiesenen Puffer und die Informationen zum Housekeeping, z. B. die Größe des Puffers. Bei kleinen Strings wird stattdessen derselbe Platz für die direkte Speicherung des String-Inhalts verwendet und vermeiden Sie jegliche dynamische Speicherzuweisung vollständig. Weitere Informationen zur Optimierung kleiner Strings (und anderer String-Optimierungen und Pessimisierungen in beträchtlicher Tiefe) finden Sie unter .... "

Und für ein weniger nützliches Beispiel siehe die lange, aber nicht schlüssige Frage gcc, striktes Aliasing und das Durchgreifen einer Vereinigung .

22
Joseph Quinsey

Nun, ein Anwendungsfall, den ich mir vorstellen kann, ist folgender:

typedef union
{
    struct
    {
        uint8_t a;
        uint8_t b;
        uint8_t c;
        uint8_t d;
    };
    uint32_t x;
} some32bittype;

Sie können dann auf die separaten 8-Bit-Teile dieses 32-Bit-Datenblocks zugreifen. Bereiten Sie sich jedoch darauf vor, möglicherweise von Endianness gebissen zu werden. 

Dies ist nur ein hypothetisches Beispiel, aber wenn Sie Daten in einem Feld in solche Bestandteile aufteilen möchten, können Sie eine Union verwenden.

Das heißt, es gibt auch eine Methode, die endian-sicher ist:

uint32_t x;
uint8_t a = (x & 0xFF000000) >> 24;

Zum Beispiel wird diese binäre Operation vom Compiler in die korrekte Endianness konvertiert.

18
user257111

Unions sind nützlich, wenn es um Daten auf Byte-Ebene (niedriger Ebene) geht. 

Kürzlich habe ich mich mit der IP-Adressmodellierung beschäftigt, die wie folgt aussieht:

// Composite structure for IP address storage
union
{
    // IPv4 @ 32-bit identifier
    // Padded 12-bytes for IPv6 compatibility
    union
    {
        struct
        {
            unsigned char _reserved[12];
            unsigned char _IpBytes[4];
        } _Raw;

        struct
        {
            unsigned char _reserved[12];
            unsigned char _o1;
            unsigned char _o2;
            unsigned char _o3;
            unsigned char _o4;    
        } _Octet;    
    } _IPv4;

    // IPv6 @ 128-bit identifier
    // Next generation internet addressing
    union
    {
        struct
        {
            unsigned char _IpBytes[16];
        } _Raw;

        struct
        {
            unsigned short _w1;
            unsigned short _w2;
            unsigned short _w3;
            unsigned short _w4;
            unsigned short _w5;
            unsigned short _w6;
            unsigned short _w7;
            unsigned short _w8;   
        } _Word;
    } _IPv6;
} _IP;
13
YeenFei

Einige Anwendungen für Gewerkschaften:

  • Stellen Sie eine allgemeine Endianness-Schnittstelle für einen unbekannten externen Host bereit.
  • Ändern Sie Floating-Point-Daten einer fremden CPU-Architektur, z. B. das Akzeptieren von VAX G_FLOATS von einer Netzwerkverbindung, und konvertieren Sie diese zur Verarbeitung in IEEE 754 long reals .
  • Bieten Sie direkten Zugriff auf einen übergeordneten Typ.
union {
      unsigned char   byte_v[16];
      long double     ld_v;
 }

Mit dieser Deklaration ist es einfach, die Hex-Byte-Werte eines long double anzuzeigen, das Vorzeichen des Exponenten zu ändern, festzustellen, ob es sich um einen Denormal-Wert handelt, oder eine lange Doppelarithmetik für eine CPU zu implementieren, die ihn nicht unterstützt usw.

  • Speicherplatz sparen, wenn Felder von bestimmten Werten abhängig sind:

    class person {  
        string name;  
    
        char gender;   // M = male, F = female, O = other  
        union {  
            date  castrated;   // for males  
            int   pregnancies; // for females  
        } gender_specific_data;
    }
    
  • Grep die Include-Dateien zur Verwendung mit Ihrem Compiler. Sie finden Dutzende bis Hunderte von Verwendungen von union:

    [[email protected] ~]$ cd /usr/include
    [[email protected] include]$ grep -w union *
    a.out.h:  union
    argp.h:   parsing options, getopt is called with the union of all the argp
    bfd.h:  union
    bfd.h:  union
    bfd.h:union internal_auxent;
    bfd.h:  (bfd *, struct bfd_symbol *, int, union internal_auxent *);
    bfd.h:  union {
    bfd.h:  /* The value of the symbol.  This really should be a union of a
    bfd.h:  union
    bfd.h:  union
    bfdlink.h:  /* A union of information depending upon the type.  */
    bfdlink.h:  union
    bfdlink.h:       this field.  This field is present in all of the union element
    bfdlink.h:       the union; this structure is a major space user in the
    bfdlink.h:  union
    bfdlink.h:  union
    curses.h:    union
    db_cxx.h:// 4201: nameless struct/union
    elf.h:  union
    elf.h:  union
    elf.h:  union
    elf.h:  union
    elf.h:typedef union
    _G_config.h:typedef union
    gcrypt.h:  union
    gcrypt.h:    union
    gcrypt.h:    union
    gmp-i386.h:  union {
    ieee754.h:union ieee754_float
    ieee754.h:union ieee754_double
    ieee754.h:union ieee854_long_double
    ifaddrs.h:  union
    jpeglib.h:  union {
    ldap.h: union mod_vals_u {
    ncurses.h:    union
    newt.h:    union {
    obstack.h:  union
    pi-file.h:  union {
    resolv.h:   union {
    signal.h:extern int sigqueue (__pid_t __pid, int __sig, __const union sigval __val)
    stdlib.h:/* Lots of hair to allow traditional BSD use of `union wait'
    stdlib.h:  (__extension__ (((union { __typeof(status) __in; int __i; }) \
    stdlib.h:/* This is the type of the argument to `wait'.  The funky union
    stdlib.h:   causes redeclarations with either `int *' or `union wait *' to be
    stdlib.h:typedef union
    stdlib.h:    union wait *__uptr;
    stdlib.h:  } __WAIT_STATUS __attribute__ ((__transparent_union__));
    thread_db.h:  union
    thread_db.h:  union
    tiffio.h:   union {
    wchar.h:  union
    xf86drm.h:typedef union _drmVBlank {
    
11
wallyk

Ein Beispiel, wenn ich eine Union verwendet habe:

class Vector
{
        union 
        {
            double _coord[3];
            struct 
            {
                double _x;
                double _y; 
                double _z;
            };

        };
...
}

dadurch kann ich auf meine Daten als Array oder auf die Elemente zugreifen.

Ich habe eine Union verwendet, damit die verschiedenen Begriffe auf denselben Wert verweisen. Bei der Bildverarbeitung kann es verwirrend werden, ob ich nun an Spalten, der Breite oder der Größe in X-Richtung gearbeitet habe. Um dieses Problem zu vermeiden, verwende ich eine Union, damit ich weiß, welche Beschreibungen zusammengehören.

   union {   // dimension from left to right   // union for the left to right dimension
        uint32_t            m_width;
        uint32_t            m_sizeX;
        uint32_t            m_columns;
    };

    union {   // dimension from top to bottom   // union for the top to bottom dimension
        uint32_t            m_height;
        uint32_t            m_sizeY;
        uint32_t            m_rows;
    };
10
DannyK

Gewerkschaften sorgen für Polymorphismus in C.

7
Null Set

Eine geniale Verwendung von Union ist die Speicherausrichtung, die ich im PCL-Quellcode (Point Cloud Library) gefunden habe. Die einzige Datenstruktur in der API kann auf zwei Architekturen abzielen: CPU mit SSE Unterstützung sowie CPU ohne SSE Unterstützung. Zum Beispiel: Die Datenstruktur für PointXYZ ist 

typedef union
{
  float data[4];
  struct
  {
    float x;
    float y;
    float z;
  };
} PointXYZ;

Die 3 Schwimmer sind mit einem zusätzlichen Schwimmer für SSE Ausrichtung aufgefüllt. So für 

PointXYZ point;

Der Benutzer kann entweder auf point.data [0] oder point.x (abhängig von der Unterstützung SSE) zugreifen, um z. B. auf die x-Koordinate zuzugreifen PCL-Dokumentation PointT-Typen

6
Shubham Verma

Das union-Schlüsselwort, solange es noch in C++ 03 verwendet wird1ist meist ein Überbleibsel der C-Tage. Das krasseste Problem ist, dass es nur mit POD funktioniert1.

Die Idee der Vereinigung ist jedoch immer noch vorhanden, und in den Boost-Bibliotheken gibt es tatsächlich eine Vereinigungsklasse:

boost::variant<std::string, Foo, Bar>

Welche hat die meisten Vorteile von union (wenn nicht alle) und fügt hinzu:

  • fähigkeit, Nicht-POD-Typen korrekt zu verwenden
  • statische Sicherheit

In der Praxis hat sich gezeigt, dass es einer Kombination von union + enum entspricht, und es wurde als schnelles Benchmarking ermittelt (während boost::any eher dem Bereich von dynamic_cast entspricht, da er RTTI verwendet).

1Unions wurden in C++ 11 aktualisiert ( Uneingeschränkte Vereinigungen ) und können jetzt Objekte mit Destruktoren enthalten, obwohl der Benutzer den Destruktor manuell (auf dem aktuell aktiven Gewerkschaftsmitglied) aufrufen muss. Es ist immer noch viel einfacher, Varianten zu verwenden.

6
Matthieu M.

Aus dem Wikipedia-Artikel zu Gewerkschaften :

Der Hauptnutzen einer Gewerkschaft ist zu , um Platz zu sparen, da er eine. viele verschiedene Arten sein zu lassen im selben Raum gespeichert. Gewerkschaften auch liefern groben Polymorphismus. Jedoch, Es gibt keine Überprüfung der Typen, also Es liegt am Programmierer, sicher zu gehen dass auf die richtigen Felder in .__ zugegriffen wird. verschiedene Kontexte. Das relevante Feld einer Union-Variable ist in der Regel bestimmt durch den Zustand anderer Variablen, möglicherweise in einem einschließenden struct.

Eine gebräuchliche C-Programmiersprache verwendet Vereinigungen, die ausgeführt werden sollen, was C++ als .__ bezeichnet. reinterpret_cast durch Zuweisung an eine Feld einer Gewerkschaft und Lesen von eine andere, wie im Code was .__ getan wird. hängt von der Rohdarstellung von .__ ab. die Werte.

3
thkala

Nehmen wir an, Sie haben n verschiedene Arten von Konfigurationen (es handelt sich lediglich um eine Menge von Variablen, die Parameter definieren). Durch Verwendung einer Auflistung der Konfigurationstypen können Sie eine Struktur definieren, die die ID des Konfigurationstyps sowie eine Vereinigung aller verschiedenen Konfigurationstypen enthält.

Überall dort, wo Sie die Konfiguration übergeben, kann die ID verwendet werden, um zu bestimmen, wie die Konfigurationsdaten interpretiert werden sollen. Wenn die Konfigurationen jedoch sehr umfangreich sind, müssen Sie keine parallelen Strukturen für jeden möglichen Speicherplatztyp haben.

1
Gavin H

In letzter Zeit wurde durch die Strict Aliasing Rule, die in der jüngsten Version des C-Standards eingeführt wurde, die bereits erhöhte Bedeutung der union s hervorgehoben.

Sie können unions do zu type-punning verwenden, ohne den C-Standard zu verletzen.
Dieses Programm hat nicht angegebenes Verhalten (da ich davon ausgegangen bin, dass float und unsigned int dieselbe Länge haben), aber nicht undefined Verhalten (siehe hier ).

#include <stdio.h> 

union float_uint
{
    float f;
    unsigned int ui;
};

int main()
{
    float v = 241;
    union float_uint fui = {.f = v};

    //May trigger UNSPECIFIED BEHAVIOR but not UNDEFINED BEHAVIOR 
    printf("Your IEEE 754 float sir: %08x\n", fui.ui);

    //This is UNDEFINED BEHAVIOR as it violates the Strict Aliasing Rule
    unsigned int* pp = (unsigned int*) &v;

    printf("Your IEEE 754 float, again, sir: %08x\n", *pp);

    return 0;
}
1
user781847

Ich möchte ein gutes praktisches Beispiel für die Verwendung von union - implementieren des Formelrechners/Interpreters oder einer Art davon in der Berechnung hinzufügen (z. B. möchten Sie während der Laufzeit modifizierbar Teile Ihrer Berechnungsformeln - lösen verwenden Gleichung numerisch - nur zum Beispiel) ..__ Sie können also Zahlen/Konstanten verschiedener Typen (Ganzzahl, Fließkommazahl, sogar komplexe Zahlen) wie folgt definieren:

struct Number{
enum NumType{int32, float, double, complex}; NumType num_t;
union{int ival; float fval; double dval; ComplexNumber cmplx_val}
}

Sie sparen also Speicher und was noch wichtiger ist - Sie vermeiden dynamische Zuordnungen für wahrscheinlich extrem große Mengen (wenn Sie viele zur Laufzeit definierte Zahlen verwenden) von kleinen Objekten (im Vergleich zu Implementierungen durch Klassenvererbung/Polymorphismus). Aber was noch interessanter ist, Sie können die Stärke des C++ - Polymorphismus (wenn Sie beispielsweise ein Fan von Double Dispatching sind;) mit dieser Art von Struktur verwenden. Fügen Sie einfach einen Dummy-Schnittstellenzeiger zu der übergeordneten Klasse aller Zahlentypen als Feld dieser Struktur hinzu und zeigen Sie auf diese Instanz anstelle von/zusätzlich zum raw-Typ, oder verwenden Sie gute alte C-Funktionszeiger.

struct NumberBase
{
virtual Add(NumberBase n);
...
}
struct NumberInt: Number
{
//implement methods assuming Number's union contains int
NumberBase Add(NumberBase n);
...
}
struct NumberDouble: Number
{
 //implement methods assuming Number's union contains double
 NumberBase Add(NumberBase n);
 ...
}
//e.t.c. for all number types/or use templates
struct Number: NumberBase{
 union{int ival; float fval; double dval; ComplexNumber cmplx_val;}
 NumberBase* num_t;
 Set(int a)
 {
 ival=a;
  //still kind of hack, hope it works because derived classes of   Number    dont add any fields
 num_t = static_cast<NumberInt>(this);
 }
}

sie können also Polymorphismus anstelle von Typprüfungen mit switch (type) verwenden - bei speichereffizienter Implementierung (keine dynamische Zuordnung kleiner Objekte) - wenn Sie es benötigen.

1
Mastermind

Von http://cplus.about.com/od/learningc/ss/lowlevel_9.htm :

Die Verwendung von Vereinigung ist sehr selten. Auf den meisten Computern ist die Größe eines Zeigers und eines int sind normalerweise gleich - dies liegt daran, dass beide passt normalerweise in ein Register in der CPU. Wenn Sie also eine schnelle und schmutzige Umwandlung eines Zeigers auf ein int oder umgekehrt, deklarieren Sie eine Union.

union intptr {   int i;   int * p; }; 
union intptr x; x.i = 1000; 
/* puts 90 at location 1000 */ 
*(x.p)=90; 

Eine andere Verwendung einer Vereinigung besteht in einem Befehls- oder Nachrichtenprotokoll, wobei Nachrichten unterschiedlicher Größe werden gesendet und empfangen. Jeder Nachrichtentyp wird halten unterschiedliche Informationen, aber jeder hat einen festen Teil (wahrscheinlich eine struct) und ein variables Teilbit. So können Sie es implementieren.

struct head {   int id;   int response;   int size; }; struct msgstring50 {    struct head fixed;    char message[50]; } struct

struct msgstring80 {Strukturkopf behoben; Zeichen Nachricht [80]; }
Struktur msgint10 {Strukturkopf fixiert; int message [10]; } struct msgack {Strukturkopf fixiert; int ok; } union messagetype {
struct msgstring50 m50; struct msgstring80 m80; struct msgint10 i10; struct msgack ack; } 

Obwohl die Gewerkschaften dieselbe Größe haben, ist es in der Praxis sinnvoll, Senden Sie nur die aussagekräftigen Daten und keinen verschwendeten Speicherplatz. Eine msgack ist nur 16 Bytes groß, während ein msgstring80 92 Bytes ist. Also wenn ein Messagetype-Variable wird initialisiert, das Größenfeld ist festgelegt nach welcher Art es ist. Dies kann dann von anderen .__ verwendet werden. Funktionen zum Übertragen der richtigen Anzahl von Bytes.

0

In den ersten Tagen von C (z. B. wie dokumentiert 1974) hatten alle Strukturen einen gemeinsamen Namensraum für ihre Mitglieder. Jeder Mitgliedsname wurde einem Typ und einem Versatz zugeordnet. Wenn "wd_woozle" am Offset 12 ein "int" war, dann wäre ein [p] Zeiger eines beliebigen Strukturtyps p->wd_woozle äquivalent zu *(int*)(((char*)p)+12). Die Sprache erforderte, dass alle Member aller Strukturtypen eindeutige Namen haben mit Ausnahme von, das die Wiederverwendung von Mitgliedsnamen explizit zulässt, wenn jede Struktur, in der sie verwendet wurden, sie als eine gemeinsame Anfangssequenz behandelte.

Die Tatsache, dass Strukturtypen gezielt verwendet werden konnten, ermöglichte es, dass sich Strukturen so verhalten, als ob sie überlappende Felder enthielten. Zum Beispiel gegebene Definitionen:

struct float1 { float f0;};
struct byte4  { char b0,b1,b2,b3; }; /* Unsigned didn't exist yet */

code könnte eine Struktur vom Typ "float1" deklarieren und dann "Members" b0 ... b3 verwenden, um auf die einzelnen darin enthaltenen Bytes zuzugreifen. Wenn die Sprache so geändert wurde, dass jede Struktur einen separaten Namespace für ihre Mitglieder erhalten würde, würde Code, der sich auf die Möglichkeit stützte, auf Dinge auf mehrere Arten zuzugreifen, brechen. Die Werte der Trennung von Namespaces für verschiedene Strukturtypen waren ausreichend, um die Anpassung des Codes an ihn zu erfordern, aber der Wert solcher Techniken war ausreichend, um die Erweiterung der Sprache zu rechtfertigen, um sie weiter zu unterstützen.

Code, der geschrieben wurde, um die Möglichkeit des Zugriffs auf den Speicher innerhalb eines struct float1 zu nutzen, als wäre es ein struct byte4, könnte durch Hinzufügen einer Deklaration in der neuen Sprache ausgeführt werden: union f1b4 { struct float1 ff; struct byte4 bb; };, deklarieren von Objekten als Typ union f1b4; anstelle von struct float1 und Ersetzen von Zugriffen auf f0, b0, b1 usw. durch ff.f0, bb.b0, bb.b1 usw. Obwohl es bessere Möglichkeiten gibt, wie Code unterstützt werden konnte, war der union-Ansatz zumindest etwas praktikabel, zumindest bei Interpretationen der Aliasing-Regeln aus dem C89-Zeitalter.

0
supercat

Unions bieten die Möglichkeit, verschiedene Arten von Daten in einem einzigen Speicherbereich zu bearbeiten, ohne maschinenunabhängige Informationen in das Programm einzubetten. __ Sie sind analog zu Variantendatensätzen in Pascal

Als Beispiel, wie es in einem Compiler-Symboltabellen-Manager zu finden ist, sei angenommen, dass eine -Konstante ein int, ein float oder ein Zeichenzeiger sein kann. Der Wert einer bestimmten Konstante .__ muss in einer Variablen des richtigen Typs gespeichert werden. Für die Tabellenverwaltung ist es jedoch am bequemsten, wenn der Wert dieselbe Speichermenge belegt und unabhängig von seinem Typ am selben Ort gespeichert wird. Dies ist der Zweck einer Union - eine einzige Variable, die einen von mehreren Typen rechtmäßig aufnehmen kann. Die Syntax basiert auf Strukturen: 

union u_tag {
     int ival;
     float fval;
     char  *sval;
} u;

Die Variable u ist groß genug, um den größten der drei Typen aufzunehmen. Die spezifische Größe ist implementierungsabhängig. Jeder dieser Typen kann u zugewiesen und dann in __.-Ausdrücken verwendet werden, sofern die Verwendung konsistent ist

0
Khushal