Warum brauchen wir C-Unions?

Lesezeit: 8 Minuten

Wann sollten Gewerkschaften verwendet werden? Warum brauchen wir sie?

  • Verwandte: Portabilität der Verwendung von Union für die Konvertierung

    – Gabriel Staples

    27. Juli um 13:38 Uhr

Benutzeravatar von Adam Rosenfield
Adam Rosenfield

Unions werden oft verwendet, um zwischen den binären Darstellungen von Ganzzahlen und Floats zu konvertieren:

union
{
  int i;
  float f;
} u;

// Convert floating-point bits to integer:
u.f = 3.14159f;
printf("As integer: %08x\n", u.i);

Obwohl dies gemäß dem C-Standard ein technisch undefiniertes Verhalten ist (Sie sollten nur das zuletzt geschriebene Feld lesen), verhält es sich in praktisch jedem Compiler auf wohldefinierte Weise.

Unions werden manchmal auch verwendet, um Pseudopolymorphismus in C zu implementieren, indem einer Struktur ein Tag gegeben wird, das angibt, welche Art von Objekt sie enthält, und dann die möglichen Typen miteinander vereint werden:

enum Type { INTS, FLOATS, DOUBLE };
struct S
{
  Type s_type;
  union
  {
    int s_ints[2];
    float s_floats[2];
    double s_double;
  };
};

void do_something(struct S *s)
{
  switch(s->s_type)
  {
    case INTS:  // do something with s->s_ints
      break;

    case FLOATS:  // do something with s->s_floats
      break;

    case DOUBLE:  // do something with s->s_double
      break;
  }
}

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

  • Anstelle von uf sollte uy stehen

    – Amit Singh Tomar

    8. September 2011 um 8:17 Uhr

  • Funktioniert das Beispiel, das Float in Integer umwandeln soll? Ich glaube nicht, da int und float in unterschiedlichen Formaten im Speicher abgelegt sind. Können Sie Ihr Beispiel erläutern?

    – spin_acht

    10. Oktober 2012 um 10:51 Uhr

  • @spin_eight: Es “konvertiert” nicht von float nach int. Eher wie “die binäre Darstellung eines Floats neu interpretieren, als wäre es ein Int”. Die Ausgabe ist nicht 3: ideone.com/MKjwon Ich bin mir jedoch nicht sicher, warum Adam als Hex druckt.

    – Endolith

    21. Februar 2013 um 17:19 Uhr

  • @ Adam Rosenfield Ich habe die Konvertierung nicht wirklich verstanden. Ich bekomme keine Ganzzahl in der Ausgabe: p

    – Das Biest

    18. Januar 2016 um 16:38 Uhr

  • Ich bin der Meinung, dass der Haftungsausschluss, dass es sich um ein undefiniertes Verhalten handelt, entfernt werden sollte. Es ist in der Tat ein definiertes Verhalten. Siehe Fußnote 82 des C99-Standards: Wenn der für den Zugriff auf den Inhalt eines Union-Objekts verwendete Member nicht derselbe ist wie der zuletzt zum Speichern eines Werts im Objekt verwendete Member, wird der entsprechende Teil der Objektdarstellung des Werts als Objektdarstellung im neuen Typ neu interpretiert wie in 6.2.6 beschrieben (ein Prozess, der manchmal als “Type Punning” bezeichnet wird). Dies könnte eine Fallendarstellung sein.

    – Christian Gibbons

    18. Juni 2018 um 20:29 Uhr

Benutzeravatar von kgiannakakis
Kgiannakakis

Unions sind besonders nützlich bei der Embedded-Programmierung oder in Situationen, in denen ein direkter Zugriff auf die Hardware/den Speicher erforderlich ist. Hier ein triviales Beispiel:

typedef union
{
    struct {
        unsigned char byte1;
        unsigned char byte2;
        unsigned char byte3;
        unsigned char byte4;
    } bytes;
    unsigned int dword;
} HW_Register;
HW_Register reg;

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

reg.dword = 0x12345678;
reg.bytes.byte3 = 4;

Endianness (Bytereihenfolge) und Prozessorarchitektur sind natürlich wichtig.

Eine weitere nützliche Funktion ist der Bit-Modifikator:

typedef union
{
    struct {
        unsigned char b1:1;
        unsigned char b2:1;
        unsigned char b3:1;
        unsigned char b4:1;
        unsigned char reserved:4;
    } bits;
    unsigned char byte;
} HW_RegisterB;
HW_RegisterB reg;

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

x = reg.bits.b2;

  • Ihre Antwort hier in Verbindung mit der Antwort von @Adam Rosenfield oben ergibt das perfekte Komplementärpaar: Sie demonstrieren die Verwendung von a Struktur innerhalb einer Unionund er demonstriert die Verwendung von a Vereinigung innerhalb einer Struktur. Es stellt sich heraus, dass ich beides auf einmal brauche: a Struktur innerhalb einer Union innerhalb einer Struktur einen ausgefallenen Message-Passing-Polymorphismus in C zwischen Threads auf einem eingebetteten System zu implementieren, und ich hätte das nicht bemerkt, wenn ich Ihre beiden Antworten nicht zusammen gesehen hätte.

    – Gabriel Staples

    21. November 2018 um 0:58 Uhr


  • Ich habe mich geirrt: Es ist eine Union innerhalb einer Struktur innerhalb einer Union innerhalb einer Struktur, links verschachtelt, um zu schreiben, wie ich das geschrieben habe, von der innersten Verschachtelung bis zur äußersten Ebene. Ich musste auf der innersten Ebene eine weitere Union hinzufügen, um Werte unterschiedlicher Datentypen zuzulassen.

    – Gabriel Staples

    21. November 2018 um 1:19 Uhr

  • Warum fängst du mit an b1 und nicht b0? Das Problem ist, dass es keine Informationen über die Bestellung gibt. In deinem Beispiel b1 könnte Bit 0 oder das höchste Bit (wahrscheinlich Bit 7) sein.

    – 12431234123412341234123

    7. September 2020 um 17:03 Uhr

  • @GabrielStaples Könntest du es bitte als Antwort mit einem Beispiel posten? Ich bin nur Neugierig.

    – Unbekannt123

    27. Juli um 12:58 Uhr


Benutzeravatar von Snips
Schnipp

Die Systemprogrammierung auf niedriger Ebene ist ein vernünftiges Beispiel.

IIRC, ich habe Unions verwendet, um Hardwareregister in die Komponentenbits aufzuschlüsseln. Sie können also auf ein 8-Bit-Register zugreifen (wie es damals war 😉 in die Komponentenbits.

(Ich habe die genaue Syntax vergessen, aber …) Diese Struktur würde es ermöglichen, auf ein Steuerregister als control_byte oder über die einzelnen Bits zuzugreifen. Es wäre wichtig sicherzustellen, dass die Bits für eine gegebene Endianness auf die richtigen Registerbits abgebildet werden.

typedef union {
    unsigned char control_byte;
    struct {
        unsigned int nibble  : 4;
        unsigned int nmi     : 1;
        unsigned int enabled : 1;
        unsigned int fired   : 1;
        unsigned int control : 1;
    };
} ControlRegister;

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

Z.B

        Connection
     /       |       \
  Network   USB     VirtualConnection

Wenn Sie möchten, dass die “Klasse” von Connection eine der oben genannten ist, können Sie Folgendes schreiben:

struct Connection
{
    int type;
    union
    {
        struct Network network;
        struct USB usb;
        struct Virtual virtual;
    }
};

Beispielverwendung in libinfinity: http://git.0x539.de/?p=infinote.git;a=blob;f=libinfinity/common/inf-session.c;h=3e887f0d63bd754c6b5ec232948027cbbf4d61fc;hb=HEAD#l74

Benutzeravatar von LeopardSkinPillBoxHat
LeopardSkinPillBoxHat

Vereinigungen ermöglichen es Datenmitgliedern, die sich gegenseitig ausschließen, denselben Speicher zu teilen. Dies ist sehr wichtig, wenn Speicher knapper ist, wie z. B. in eingebetteten Systemen.

Im folgenden Beispiel:

union {
   int a;
   int b;
   int c;
} myUnion;

Diese Vereinigung nimmt den Platz eines einzelnen int ein, anstatt 3 separate int-Werte. Wenn der Benutzer den Wert von festlegt aund legen Sie dann den Wert von fest bwürde es den Wert von überschreiben a da beide den gleichen Speicherplatz teilen.

Viele Verwendungen. Mach einfach grep union /usr/include/* oder in ähnlichen Verzeichnissen. Die meisten Fälle union ist eingewickelt struct und ein Mitglied der Struktur teilt mit, auf welches Element in der Union zugegriffen werden soll. Zum Beispiel Kasse man elf für reale Implementierungen.

Das ist das Grundprinzip:

struct _mydata {
    int which_one;
    union _data {
            int a;
            float b;
            char c;
    } foo;
} bar;

switch (bar.which_one)
{
   case INTEGER  :  /* access bar.foo.a;*/ break;
   case FLOATING :  /* access bar.foo.b;*/ break;
   case CHARACTER:  /* access bar.foo.c;*/ break;
}

Benutzeravatar von jyz
jyz

Hier ist ein Beispiel für eine Vereinigung aus meiner eigenen Codebasis (aus dem Gedächtnis und umschrieben, damit es möglicherweise nicht genau ist). Es wurde verwendet, um Sprachelemente in einem von mir gebauten Interpreter zu speichern. Zum Beispiel der folgende Code:

set a to b times 7.

besteht aus folgenden Sprachelementen:

  • Symbol[set]
  • Variable[a]
  • Symbol[to]
  • Variable[b]
  • Symbol[times]
  • Konstante[7]
  • Symbol[.]

Sprachelemente wurden definiert als ‘#define‘Werte also:

#define ELEM_SYM_SET        0
#define ELEM_SYM_TO         1
#define ELEM_SYM_TIMES      2
#define ELEM_SYM_FULLSTOP   3
#define ELEM_VARIABLE     100
#define ELEM_CONSTANT     101

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

typedef struct {
    int typ;
    union {
        char *str;
        int   val;
    }
} tElem;

dann war die Größe jedes Elements die Größe der maximalen Vereinigung (4 Bytes für den Typ und 4 Bytes für die Vereinigung, obwohl dies typische Werte sind, die tatsächlich Größen hängen von der Implementierung ab).

Um ein “Set”-Element zu erstellen, würden Sie Folgendes verwenden:

tElem e;
e.typ = ELEM_SYM_SET;

Um eine „Variable[b]”-Element würden Sie verwenden:

tElem e;
e.typ = ELEM_VARIABLE;
e.str = strdup ("b");   // make sure you free this later

Um eine „konstante[7]”-Element würden Sie verwenden:

tElem e;
e.typ = ELEM_CONSTANT;
e.val = 7;

und Sie könnten es leicht erweitern, um Gleitkommazahlen (float flt) oder rationale (struct ratnl {int num; int denom;}) und andere Arten.

Die Grundvoraussetzung ist, dass die str und val sind im Speicher nicht zusammenhängend, sie überlappen sich tatsächlich, also ist es eine Möglichkeit, eine andere Sicht auf denselben Speicherblock zu bekommen, wie hier dargestellt, wo die Struktur am Speicherort basiert 0x1010 und ganze Zahlen und Zeiger sind beide 4 Bytes:

       +-----------+
0x1010 |           |
0x1011 |    typ    |
0x1012 |           |
0x1013 |           |
       +-----+-----+
0x1014 |     |     |
0x1015 | str | val |
0x1016 |     |     |
0x1017 |     |     |
       +-----+-----+

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

       +-------+
0x1010 |       |
0x1011 |  typ  |
0x1012 |       |
0x1013 |       |
       +-------+
0x1014 |       |
0x1015 |  str  |
0x1016 |       |
0x1017 |       |
       +-------+
0x1018 |       |
0x1019 |  val  |
0x101A |       |
0x101B |       |
       +-------+

  • Sollte die make sure you free this later Kommentar aus dem konstanten Element entfernt werden?

    – Trevor

    6. März 2013 um 12:21 Uhr

  • Ja, @Trevor, obwohl ich nicht glauben kann, dass du die erste Person bist, die es in den letzten 4+ Jahren gesehen hat 🙂 Behoben, und danke dafür.

    – paxdiablo

    6. März 2013 um 12:38 Uhr

1426830cookie-checkWarum brauchen wir C-Unions?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy