it-swarm.com.de

größe einer Union in C/C++

Welche Größe hat die Union in C/C++? Ist es die Größe des größten Datentyps darin? Wenn ja, wie berechnet der Compiler, wie der Stapelzeiger verschoben wird, wenn einer der kleineren Datentypen der Union aktiv ist?

37
Naveen

Der Standard beantwortet alle Fragen in Abschnitt 9.5 des C++ - Standards oder Abschnitt 6.5.2.3, Absatz 5 des C99-Standards (oder Absatz 6 des C11-Standards):

In einer Union kann höchstens eines der Datenmitglieder zu einem beliebigen Zeitpunkt aktiv sein, das heißt, der Wert von höchstens einem der Datenelemente kann jederzeit in einer Union gespeichert werden. [Anmerkung: Es wird eine besondere Garantie gegeben, um die Verwendung von Vereinigungen zu vereinfachen: Wenn eine POD-Vereinigung mehrere POD-Strukturen enthält, die eine gemeinsame Anfangssequenz (9.2) haben, und wenn ein Objekt dieses POD-Vereinigungstyps eine der folgenden Funktionen enthält Bei den POD-Struk- turen ist es erlaubt, die gemeinsame Anfangssequenz eines der POD-Strukture zu prüfen. siehe 9.2. ] Die Größe einer Gewerkschaft reicht aus, um die größten ihrer Datenmitglieder aufzunehmen. Jedes Datenmitglied wird so zugewiesen, als wäre es das einzige Mitglied einer Struktur. 

Das heißt, jedes Mitglied hat den gleichen Speicherbereich. Dort ist is höchstens ein Mitglied aktiv, aber Sie können nicht herausfinden, welches Mitglied. Sie müssen diese Informationen an einem anderen Ort selbst speichern. Wenn Sie ein solches Flag zusätzlich zur Union speichern (z. B. eine Struktur mit einer Ganzzahl als Typflag und einer Union als Datenspeicher), erhalten Sie eine so genannte "diskriminierte Union": Eine Union, die den Typ kennt es ist derzeit der "aktive".

Eine häufige Verwendung ist in Lexern, wo Sie verschiedene Token haben können, aber je nach Token müssen Sie unterschiedliche Informationen speichern (line in jede Struktur geben, um zu zeigen, was eine gemeinsame Anfangssequenz ist):

struct tokeni {
    int token; /* type tag */
    union {
        struct { int line; } noVal;
        struct { int line; int val; } intVal;
        struct { int line; struct string val; } stringVal;
    } data;
};

Mit dem Standard können Sie auf line jedes Mitglieds zugreifen, da dies die übliche Anfangssequenz jedes Mitglieds ist. 

Es gibt Compiler-Erweiterungen, die den Zugriff auf alle Member zulassen, wobei nicht berücksichtigt wird, für welchen der Wert derzeit gespeichert ist. Dies ermöglicht eine effiziente Neuinterpretation gespeicherter Bits mit unterschiedlichen Typen unter den einzelnen Mitgliedern. Zum Beispiel kann Folgendes verwendet werden, um eine Float-Variable in 2 vorzeichenlose Shorts zu zerlegen:

union float_cast { unsigned short s[2]; float f; };

Das kann sehr nützlich sein, wenn Sie Code auf niedriger Ebene schreiben. Wenn der Compiler diese Erweiterung nicht unterstützt, Sie dies aber trotzdem tun, schreiben Sie Code, dessen Ergebnisse nicht definiert sind. Stellen Sie also sicher, dass Ihr Compiler dies unterstützt, wenn Sie diesen Trick verwenden.

Eine Variable union nimmt immer so viel Speicherplatz in Anspruch, wie das größte Element. Es ist egal, was gerade verwendet wird.

union {
  short x;
  int y;
  long long z;
}

Eine Instanz der obigen union benötigt immer mindestens einen long long zur Speicherung.

Randnotiz: Wie von Stefano festgestellt, hängt der tatsächliche Speicherplatz, den jeder Typ (union, struct, class) hat, von anderen Problemen ab, wie z. Ich habe das nicht der Einfachheit halber durchgegangen, da ich nur sagen wollte, dass eine Gewerkschaft den größten Punkt berücksichtigt. Es ist wichtig zu wissen, dass die tatsächliche Größe do von der Ausrichtung abhängt.

54
Mehrdad Afshari

Das hängt vom Compiler und von den Optionen ab.

int main() {
  union {
    char all[13];
    int foo;
  } record;

printf("%d\n",sizeof(record.all));
printf("%d\n",sizeof(record.foo));
printf("%d\n",sizeof(record));

}

Dies gibt aus:

13 4 16

Wenn ich mich recht erinnere, hängt es von der Ausrichtung ab, die der Compiler in den zugewiesenen Speicherplatz legt. Sofern Sie keine spezielle Option verwenden, fügt der Compiler also Padding in Ihren Union-Bereich ein.

edit: bei gcc musst du eine pragma-Direktive verwenden

int main() {
#pragma pack(Push, 1)
      union {
           char all[13];
           int foo;
      } record;
#pragma pack(pop)

      printf("%d\n",sizeof(record.all));
      printf("%d\n",sizeof(record.foo));
      printf("%d\n",sizeof(record));

}

diese Ausgänge

13 4 13

Sie können es auch von der Zerlegung aus sehen (zur besseren Übersicht einige printf entfernt)

  0x00001fd2 <main+0>:    Push   %ebp             |  0x00001fd2 <main+0>:    Push   %ebp
  0x00001fd3 <main+1>:    mov    %esp,%ebp        |  0x00001fd3 <main+1>:    mov    %esp,%ebp
  0x00001fd5 <main+3>:    Push   %ebx             |  0x00001fd5 <main+3>:    Push   %ebx
  0x00001fd6 <main+4>:    sub    $0x24,%esp       |  0x00001fd6 <main+4>:    sub    $0x24,%esp
  0x00001fd9 <main+7>:    call   0x1fde <main+12> |  0x00001fd9 <main+7>:    call   0x1fde <main+12>
  0x00001fde <main+12>:   pop    %ebx             |  0x00001fde <main+12>:   pop    %ebx
  0x00001fdf <main+13>:   movl   $0xd,0x4(%esp)   |  0x00001fdf <main+13>:   movl   $0x10,0x4(%esp)                                         
  0x00001fe7 <main+21>:   lea    0x1d(%ebx),%eax  |  0x00001fe7 <main+21>:   lea    0x1d(%ebx),%eax
  0x00001fed <main+27>:   mov    %eax,(%esp)      |  0x00001fed <main+27>:   mov    %eax,(%esp)
  0x00001ff0 <main+30>:   call  0x3005 <printf>   |  0x00001ff0 <main+30>:   call   0x3005 <printf>
  0x00001ff5 <main+35>:   add    $0x24,%esp       |  0x00001ff5 <main+35>:   add    $0x24,%esp
  0x00001ff8 <main+38>:   pop    %ebx             |  0x00001ff8 <main+38>:   pop    %ebx
  0x00001ff9 <main+39>:   leave                   |  0x00001ff9 <main+39>:   leave
  0x00001ffa <main+40>:   ret                     |  0x00001ffa <main+40>:   ret    

Der einzige Unterschied besteht in main + 13, wo der Compiler 0xd anstelle von 0x10 auf dem Stack zuordnet

15
Stefano Borini

Es gibt keine Vorstellung von einem aktiven Datentyp für eine Union. Sie können jedes "Mitglied" der Gewerkschaft lesen und schreiben. Es liegt an Ihnen, zu interpretieren, was Sie bekommen.

Daher ist die Größe einer Vereinigung immer die Größe des größten Datentyps.

10
mouviciel

Die Größe entspricht mindestens der größten Zusammenstellungsart. Es gibt kein Konzept eines "aktiven" Typs.

3
anon

Sie sollten eine Union wirklich als Container für den größten Datentyp in ihr betrachten, kombiniert mit einer Abkürzung für eine Besetzung. Wenn Sie eines der kleineren Member verwenden, ist der nicht verwendete Speicherplatz immer noch vorhanden, bleibt aber einfach ungenutzt. 

Sie sehen oft, dass dies in Kombination mit ioctl () - Aufrufen unter Unix verwendet wird. Alle ioctl () - Aufrufe übergeben dieselbe Struktur, die eine Vereinigung aller möglichen Antworten enthält. Z.B. Dieses Beispiel stammt aus /usr/include/linux/if.h und diese Struktur wird in ioctl () zum Konfigurieren/Abfragen des Status einer Ethernet-Schnittstelle verwendet. Die Anforderungsparameter definieren, welcher Teil der Vereinigung tatsächlich ist in Benutzung:

struct ifreq 
{
#define IFHWADDRLEN 6
    union
    {
        char    ifrn_name[IFNAMSIZ];        /* if name, e.g. "en0" */
    } ifr_ifrn;

    union {
        struct  sockaddr ifru_addr;
        struct  sockaddr ifru_dstaddr;
        struct  sockaddr ifru_broadaddr;
        struct  sockaddr ifru_netmask;
        struct  sockaddr ifru_hwaddr;
        short   ifru_flags;
        int ifru_ivalue;
        int ifru_mtu;
        struct  ifmap ifru_map;
        char    ifru_slave[IFNAMSIZ];   /* Just fits the size */
        char    ifru_newname[IFNAMSIZ];
        void *  ifru_data;
        struct  if_settings ifru_settings;
    } ifr_ifru;
};
2
amo-ej1
  1. Die Größe des größten Mitglieds.

  2. Deshalb sind Gewerkschaften normalerweise in einer Struktur sinnvoll, die über ein Flag verfügt, das das "aktive" Mitglied ist.

Beispiel:

struct ONE_OF_MANY {
    enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
    union { short x; int y; long long z; };
};
0
pyon

Welche Größe hat die Union in C/C++? Ist es die Größe des größten Datentyp drin?

Ja , Die Größe der Gewerkschaft ist die Größe ihres größten Mitglieds.

Zum Beispiel : 

#include<stdio.h>

union un
{
    char c;
    int i;
    float f;
    double d;
};

int main()
{
    union un u1;
    printf("sizeof union u1 : %ld\n",sizeof(u1));
    return 0;
}

Ausgabe :

sizeof union u1 : 8
sizeof double d : 8

Hier ist das größte Mitglied double. Beide haben die Größe 8. Wie sizeof Ihnen richtig gesagt hat, ist die Größe der Vereinigung in der Tat 8.

wie berechnet der Compiler, wie der Stapelzeiger verschoben werden soll, wenn ein ist der kleinere Datentyp der Vereinigung aktiv?

Es wird intern vom Compiler behandelt. Angenommen, wir greifen auf ein Datenmitglied von union zu. Dann können wir nicht auf ein anderes Datenmitglied zugreifen, da wir auf ein einzelnes Datenmitglied von union zugreifen können, da jedes Datenmitglied denselben Speicher verwendet. Durch die Verwendung von Union können wir viel wertvollen Speicherplatz sparen.

0
M.S Chaudhari