Union Frsge



  • Angenommen ich habe das:

    struct s {int a, int b};
    union u {struct s s, int c};

    Ist sichergestellt, dass in der Union s.a und c "übereinander" liegen?

    Thanks im Voraus ... 🙂



  • ja



  • De facto ja. De jure nein. Im Standard heißt es:

    ISO/IEC 9899:1999 6.5.2.3 (5) schrieb:

    One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the complete type of the union is visible. Two structures share a common initial sequence if corresponding members have compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

    (Hervorhebung von mir)

    Konkret bedeutet das: Wenn du

    struct s { int a, int b };
    struct s2 { int c; }
    union u { struct s s, struct s2 s2 }; 
    
    union u u;
    

    hast, ist u.s.a == u.s2.c und &u.s.a == &u.s2.c vom Standard garantiert, sofern dem betreffenden Code bekannt ist, dass die Zeiger, mit denen er arbeitet, in diesen Teil einer Union zeigen. Interessant ist hier

    ISO/IEC 9899:1999 6.5.2.3 (8) schrieb:

    The following is not a valid fragment (because the union type is not visible within function f): ⚠

    struct t1 { int m; };
    struct t2 { int m; };
    
    int f(struct t1 * p1, struct t2 * p2)
    {
      if (p1->m < 0)
        p2->m = -p2->m;
      return p1->m;
    }
    
    int g()
    {
      union {
        struct t1 s1;
        struct t2 s2;
      } u;
    
      /* ... */
    
      return f(&u.s1, &u.s2);
    }
    

    Worum es hier geht, sind die Strict-Aliasing-Regeln, die es einem optimierenden Compiler erlauben, in Funktion f davon auszugehen, dass p1 und p2 auf verschiedene Speicherbereiche zeigen und entsprechend zu optimieren (also den Rückgabewert beispielsweise aus einem Register zu beziehen und die Änderung noch nicht mitzukriegen). Wenn der Compiler weiß, dass das Struct am Anfang einer Union stehen kann, darf er diese Optimierung aufgrund dieses Absatzes nicht durchführen, sonst schon.

    Für Basisdatentypen wird vom Compiler nicht verlangt, diese Prüfung vorher durchzuführen. ⚠ Dementsprechend darf ein standardkonformer Compiler dir für

    struct s { int a; int b; };
    union u { struct s s; int c; }
    
    union u u;
    
    u.c = 0;
    int *p = &u.c;
    
    u.s.a = 0xf001;
    
    printf("%d\n", *p);
    

    0 ausgeben.

    In der Praxis passiert das nicht, weil es eine Menge plattformabhängigen Code gibt, der per Union zwischen völlig verschiedenen Typen hin- und herpunnt (etwa ints und doubles), aber vom C-Standard sind derlei Kinkerlitzchen eigentlich nicht gedeckt. Gleiches gilt für deine Idee hier.


  • Mod

    Anders formuliert, handelt es sich um zwei verschiedene Fragen:
    1. Überlappen die Speicherbereiche der int-Variablen - hier ist die Antwort eindeutig ja, und die Überlappung ist exakt.

    2. Ist es zulässig, nachdem ein Wert in einem solchen int gespeichert wurde, einen Ausdruck zu verwenden, der auf das andere int verweist, um auf den (darin) gespeicherten Wert zuzugreifen - und um welchen Wert handelt es sich dabei. Diese Frage ist leider recht subtil, s.o. seldons Antwort.

    Wer ein bisschen mehr wissen will, kann sich z.B. auch mit n1637 beschäftigen.

    Nebenbei bemerkt, gibt es in C++ die EInschränkung "anywhere that a declaration of the complete type of the union is visible" nicht. In C89 und K&R-C existiert der gesamte Absatz nicht - dort kann man davon ausgehen, das diese Form des Aliasings implizit ohne Einschränkung erlaubt ist.



  • Vielen Dank. Ihr habt mir sehr geholfen.


Anmelden zum Antworten