Namespaces
Variants

Struct and union initialization

From cppreference.net

Beim Initialisieren eines Objekts vom struct - oder union -Typ muss der Initialisierer eine nicht-leere, (bis C23) geschweifte Klammern umschlossene, kommagetrennte Liste von Initialisierern für die Member sein:

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

wobei der designator eine Sequenz (durch Leerzeichen getrennt oder benachbart) von einzelnen Mitglied-Designatoren der Form . member und array designators der Form [ index ] ist.

Alle Member, die nicht explizit initialisiert werden, sind empty-initialized .

Inhaltsverzeichnis

Erklärung

Beim Initialisieren einer union muss die Initialisiererliste nur ein Element enthalten, das das erste Element der union initialisiert sofern kein designierter Initialisierer verwendet wird (seit C99) .

union { int x; char c[4]; }
  u = {1},           // macht u.x aktiv mit Wert 1
 u2 = { .c={'\1'} }; // macht u2.c aktiv mit Wert {'\1','\0','\0','\0'}

Beim Initialisieren einer struct initialisiert der erste Initialisierer in der Liste das erste deklarierte Mitglied (sofern kein Designator angegeben ist) (seit C99) , und alle nachfolgenden Initialisierer ohne Designatoren (seit C99) initialisieren die struct-Mitglieder, die nach dem durch den vorherigen Ausdruck initialisierten Mitglied 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 };      // Reihenfolge der Elemente in div_t kann variieren

Ein Designator bewirkt, dass der folgende Initialisierer das durch den Designator beschriebene Strukturelement initialisiert. Die Initialisierung setzt sich dann vorwärts in der Reihenfolge der Deklaration fort, beginnend mit dem nächsten Element, das nach dem durch den Designator beschriebenen Element deklariert ist.

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 bereitzustellen.

Verschachtelte Initialisierung

Wenn die Mitglieder der Struktur oder Union Arrays, Strukturen oder Unions sind, sind die entsprechenden Initialisierer in der geschweiften Klammer-Initialisiererliste alle Initialisierer, die für diese Mitglieder gültig sind, mit der Ausnahme, dass ihre geschweiften 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 , sofern keine Designatoren verwendet werden (seit C99) : Array-Elemente in Index-Reihenfolge, Struct-Member in Deklarationsreihenfolge, nur das erste deklarierte Member einer Union. Die Subobjekte innerhalb des aktuellen Objekts, die nicht explizit durch die schließende Klammer initialisiert werden, sind 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 = { // Beginn der Initialisierungsliste für struct example
                     { // Beginn der Initialisierungsliste für ex.addr
                        80 // Initialisiert das einzige Mitglied der Struktur
                     }, // Ende der Initialisierungsliste für ex.addr
                     { // Beginn der Initialisierungsliste für ex.in_u
                        {127,0,0,1} // Initialisiert das erste Element der Union
                     } };

Wenn der geschachtelte Initialisierer nicht mit einer öffnenden Klammer beginnt, werden nur genügend Initialisierer aus der Liste entnommen, um die Elemente oder Mitglieder des Mitgliedsarrays, der Struktur oder der Union zu berücksichtigen; alle verbleibenden Initialisierer werden zur Initialisierung des nächsten Strukturmitglieds verwendet:

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

Wenn Designatoren verschachtelt sind, folgen die Designatoren für die Mitglieder den Designatoren für die umschließenden Structs/Unions/Arrays. Innerhalb jeder verschachtelten geklammerten Initialisierungsliste bezieht sich der äußerste Designator auf das aktuelle Objekt und wählt nur das Unterobjekt aus, das innerhalb des aktuellen Objekts initialisiert werden soll.

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 explizit zweimal initialisiert wird (was bei Verwendung von Designatoren vorkommen 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 sie früher in der Initialisierungsliste erschien (wählen Sie clang, um die korrekte Ausgabe zu sehen):

#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 jedoch ein Initialisierer mit einer linken öffnenden Klammer beginnt, wird sein aktuelles Objekt vollständig neu initialisiert und alle vorherigen expliziten Initialisierer für eines seiner 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)

Hinweise

Die Initialisierungsliste kann ein nachgestelltes Komma haben, das ignoriert wird.

struct {double x,y;} p = {1.0,
                          2.0, // nachgestelltes Komma OK
                          };
Nur der Kommentar "// trailing comma OK" wurde ins Deutsche übersetzt als "// nachgestelltes Komma OK". Alle HTML-Tags, Attribute und der C++-Code bleiben unverändert.

In C darf die Initialisiererliste in geschweiften Klammern nicht leer sein (zu beachten ist, dass C++ leere Listen erlaubt und dass eine struct in C nicht leer sein kann):

(bis C23)

Die Initialisiererliste kann in C wie in C++ leer sein:

(seit C23)
struct {int n;} s = {0}; // OK
struct {int n;} s = {}; // Fehler bis C23: Initialisierungsliste darf nicht leer sein
                        // OK ab C23: s.n wird auf 0 initialisiert
struct {} s = {}; // Fehler: Struktur darf nicht leer sein

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 unbestimmt sequenziert (aber nicht in C++ seit C++11):

int n = 1;
struct {int x,y;} p = {n++, n++}; // unspezifiziert, aber wohldefiniertes Verhalten:
                                  // n wird zweimal in beliebiger Reihenfolge inkrementiert
                                  // p gleich {1,2} und {2,1} sind beide gültig
(seit C99)

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

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 Initialisierung

Siehe auch

C++ Dokumentation für Aggregate initialization