Namensräume
Varianten
Aktionen

Struktur- und Union-Initialisierung

Von cppreference.com
< c‎ | Sprache

Bei der Initialisierung eines Objekts vom Typ struct oder union muss der Initialisierer eine nicht-leere,(bis C23) in geschweiften Klammern eingeschlossene, durch Kommas getrennte Liste von Initialisierern für die Member sein.

= { Ausdruck , ... } (1) (bis C99)
= { Selektor(optional) Ausdruck , ... } (2) (seit C99)
= { } (3) (seit C23)

wobei der Selektor eine Sequenz (durch Leerzeichen getrennt oder aneinandergereiht) von einzelnen Member-Selektoren der Form . member und Array-Selektoren der Form [ index ] ist.

Alle Member, die nicht explizit initialisiert werden, werden leer initialisiert.

Inhalt

[bearbeiten] Erklärung

Bei der Initialisierung eines union muss die Initialisierungsliste nur ein Mitglied haben, das das erste Mitglied des union initialisiert, es sei denn, es wird ein benannter Initialisierer verwendet(seit C99).

union { int x; char c[4]; }
  u = {1},           // makes u.x active with value 1
 u2 = { .c={'\1'} }; // makes u2.c active with value {'\1','\0','\0','\0'}

Bei der Initialisierung eines struct initialisiert der erste Initialisierer in der Liste das erste deklarierte Mitglied (es sei denn, es wird ein Selektor angegeben)(seit C99), und alle nachfolgenden Initialisierer ohne Selektoren (seit C99)initialisieren die struct-Member, die nach dem durch den vorherigen Ausdruck initialisierten Member deklariert wurden.

struct point {double x,y,z;} p = {1.2, 1.3}; // p.x=1.2, p.y=1.3, p.z=0.0
div_t answer = {.quot = 2, .rem = -1 };      // order of elements in div_t may vary

Ein Selektor bewirkt, dass der folgende Initialisierer den durch den Selektor beschriebenen struct-Member initialisiert. Die Initialisierung wird dann in Reihenfolge der Deklaration fortgesetzt, beginnend mit dem Element, das nach dem durch den Selektor beschriebenen deklariert wurde.

struct {int sec,min,hour,day,mon,year;} z
   = {.day=31,12,2014,.sec=30,15,17}; // initializes z to {30,15,17,31,12,2014}
(seit C99)

Es ist ein Fehler, mehr Initialisierer als Member anzugeben.

[bearbeiten] Verschachtelte Initialisierung

Wenn die Member des struct oder union Arrays, structs oder unions sind, sind die entsprechenden Initialisierer in der in geschweifte Klammern eingeschlossenen Liste von Initialisierern alle Initialisierer, die für diese Member gültig sind, mit der Ausnahme, dass ihre Klammern wie folgt weggelassen werden können:

Wenn der verschachtelte Initialisierer mit einer öffnenden Klammer beginnt, initialisiert der gesamte verschachtelte Initialisierer bis zu seiner schließenden Klammer das entsprechende Member-Objekt. Jede linke öffnende Klammer etabliert ein neues aktuelles Objekt. Die Member des aktuellen Objekts werden in ihrer natürlichen Reihenfolge initialisiert, es sei denn, es werden Selektoren verwendet(seit C99): Array-Elemente in Indexreihenfolge, struct-Member in Deklarationsreihenfolge, nur das erste deklarierte Mitglied eines union. Die Unterobjekte innerhalb des aktuellen Objekts, die nicht explizit durch die schließende Klammer initialisiert werden, werden leer initialisiert.

struct example {
    struct addr_t {
       uint32_t port;
    } addr;
    union {
       uint8_t a8[4];
       uint16_t a16[2];
    } in_u;
};
struct example ex = { // start of initializer list for struct example
                     { // start of initializer list for ex.addr
                        80 // initialized struct's only member
                     }, // end of initializer list for ex.addr
                     { // start of initializer-list for ex.in_u
                        {127,0,0,1} // initializes first element of the union
                     } };

Wenn der verschachtelte Initialisierer nicht mit einer öffnenden Klammer beginnt, werden nur genügend Initialisierer aus der Liste genommen, um die Elemente oder Member des Member-Arrays, structs oder unions zu berücksichtigen; verbleibende Initialisierer werden dem nächsten struct-Member zur Initialisierung überlassen.

struct example ex = {80, 127, 0, 0, 1}; // 80 initializes ex.addr.port
                                        // 127 initializes ex.in_u.a8[0]
                                        // 0 initializes ex.in_u.a8[1]
                                        // 0 initializes ex.in_u.a8[2]
                                        // 1 initializes ex.in_u.a8[3]

Wenn Selektoren verschachtelt sind, folgen die Selektoren für die Member den Selektoren für die umschließenden structs/unions/arrays. Innerhalb jeder verschachtelten geklammerten Initialisierungsliste bezieht sich der äußerste Selektor auf das aktuelle Objekt und wählt nur das Unterobjekt aus, das innerhalb des aktuellen Objekts zu initialisieren ist.

struct example ex2 = { // current object is ex2, designators are for members of example
                       .in_u.a8[0]=127, 0, 0, 1, .addr=80}; 
struct example ex3 = {80, .in_u={ // changes current object to the union ex.in_u
                           127,
                           .a8[2]=1 // this designator refers to the member of in_u
                      } };

Wenn ein Unterobjekt zweimal explizit initialisiert wird (was bei Verwendung von Selektoren passieren kann), wird der Initialisierer verwendet, der später in der Liste erscheint (der frühere Initialisierer wird möglicherweise nicht ausgewertet).

struct {int n;} s = {printf("a\n"), // this may be printed or skipped
                     .n=printf("b\n")}; // always printed

Obwohl alle nicht initialisierten Unterobjekte implizit initialisiert werden, überschreibt die implizite Initialisierung eines Unterobjekts niemals die explizite Initialisierung desselben Unterobjekts, wenn es früher in der Initialisierungsliste erschien (wählen Sie clang, um die korrekte Ausgabe zu beobachten).

#include <stdio.h>
typedef struct { int k; int l; int a[2]; } T;
typedef struct { int i;  T t; } S;
T x = {.l = 43, .k = 42, .a[1] = 19, .a[0] = 18 };
 // x initialized to {42, 43, {18, 19} }
int main(void)
{
    S l = { 1,          // initializes l.i to 1
           .t = x,      // initializes l.t to {42, 43, {18, 19} }
           .t.l = 41,   // changes l.t to {42, 41, {18, 19} }
           .t.a[1] = 17 // changes l.t to {42, 41, {18, 17} }
          };
    printf("l.t.k is %d\n", l.t.k); // .t = x sets l.t.k to 42 explicitly
                                    // .t.l = 41 would zero out l.t.k implicitly
}

Ausgabe

l.t.k is 42

Wenn ein Initialisierer jedoch mit einer linken öffnenden Klammer beginnt, wird sein aktuelles Objekt vollständig neu initialisiert und alle vorherigen expliziten Initialisierer für seine Unterobjekte werden ignoriert.

struct fred { char s[4]; int n; };
struct fred x[ ] = { { { "abc" }, 1 }, // inits x[0] to { {'a','b','c','\0'}, 1 }
                      [0].s[0] = 'q'   // changes x[0] to { {'q','b','c','\0'}, 1 }
                   };
struct fred y[ ] = { { { "abc" }, 1 }, // inits y[0] to { {'a','b','c','\0'}, 1 }
                     [0] = { // current object is now the entire y[0] object
                             .s[0] = 'q' 
                            } // replaces y[0] with { {'q','\0','\0','\0'}, 0 }
                    };
(seit C99)

[bearbeiten] Hinweise

Die Initialisierungsliste kann eine nachgestellte Komma haben, die ignoriert wird.

struct {double x,y;} p = {1.0,
                          2.0, // trailing comma OK
                          };

In C kann die geklammerte Liste von Initialisierern nicht leer sein (beachten Sie, dass C++ leere Listen zulässt und dass ein struct in C nicht leer sein kann).

(bis C23)

Die Initialisierungsliste kann in C wie in C++ leer sein.

(seit C23)
struct {int n;} s = {0}; // OK
struct {int n;} s = {}; // Error until C23: initializer-list cannot be empty
                        // OK since C23: s.n is initialized to 0
struct {} s = {}; // Error: struct cannot be empty

Jeder Ausdruck in der Initialisierungsliste muss ein konstanter Ausdruck sein, wenn Aggregate beliebiger Speicherdauer initialisiert werden.

(bis C99)

Wie bei allen anderen Initialisierungen muss jeder Ausdruck in der Initialisierungsliste ein konstanter Ausdruck sein, wenn Aggregate mit statischer oder Thread-lokaler(seit C11) Speicherdauer initialisiert werden.

static struct {char* p} s = {malloc(1)}; // error

Die Auswertungsreihenfolge der Teilausdrücke in jedem Initialisierer ist indeterminiert sequenziert (jedoch nicht in C++ seit C++11).

int n = 1;
struct {int x,y;} p = {n++, n++}; // unspecified, but well-defined behavior:
                                  // n is incremented twice in arbitrary order
                                  // p equal {1,2} and {2,1} are both valid
(seit C99)

[bearbeiten] Beispiel

#include <stdio.h>
#include <time.h>
 
int main(void)
{
    char buff[70];
    // designated initializers simplify the use of structs whose
    // order of members is unspecified
    struct tm my_time = { .tm_year=2012-1900, .tm_mon=9, .tm_mday=9,
                          .tm_hour=8, .tm_min=10, .tm_sec=20 };
    strftime(buff, sizeof buff, "%A %c", &my_time);
    puts(buff);
}

Mögliche Ausgabe

Sunday Sun Oct  9 08:10:20 2012

[bearbeiten] Referenzen

  • C17-Standard (ISO/IEC 9899:2018)
  • 6.7.9/12-39 Initialisierung (S. 101-105)
  • C11-Standard (ISO/IEC 9899:2011)
  • 6.7.9/12-38 Initialisierung (S. 140-144)
  • C99-Standard (ISO/IEC 9899:1999)
  • 6.7.8/12-38 Initialisierung (S. 126-130)
  • C89/C90-Standard (ISO/IEC 9899:1990)
  • 6.5.7 Initialization

[bearbeiten] Siehe auch

C++-Dokumentation für Aggregatinitialisierung