Warum benötige ich keine 3 Ebenen von Klammern, um 3 Ebenen von Arrays zu initialisieren?

8

Ich bin auf dieses Beispiel gestoßen

struct sct
{
    int t[2];
};

struct str
{
    sct t[2];
};

int main()
{
    str t[2] = { {0, 2, 4, 6}, {1, 3, 5, 7} }; //Who does this work?

    cout << t[1].t[0].t[1] << t[0].t[1].t[0];     

    return 0;
}

Dies kompiliert und läuft gut. Es gibt die Ausgabe34

Ich habe erwartet, dass die Syntax für die Initialisierung wie folgt lautet:

str t[2] = {  { {0, 2},{4, 6} }, { {1, 3},{5, 7} }   };

Anstatt

 { {0, 2, 4, 6}, {1, 3, 5, 7} };

Aber das gab:

In function 'int main()':
error: too many initializers for 'str'

Kann jemand erklären warum?

Dies ist ein Bild, um zu veranschaulichen, wie ich das sehe: Geben Sie hier die Bildbeschreibung ein

Wie soll ich denken, wenn es darum geht, verschachtelte Strukturen zu initialisieren?

Cătălina Sîrbu
quelle
3
Wenn Sie Fragen zu Erstellungsfehlern stellen, geben Sie bitte immer die tatsächlichen Fehler an, die Sie erhalten, und kopieren Sie sie vollständig und vollständig als Text.
Ein Programmierer

Antworten:

7

Dies sieht aus wie ein einfacher Tippfehler, aber die Situation ist komplex genug, um ihn Schritt für Schritt anzugehen.

Lassen Sie mich zunächst die Lösung zeigen, die zu funktionieren scheint:

int main()
{
    str t[2] = { { { {0, 2}, {4, 6} } }, { { {1, 3}, {5, 7} } } };
    cout << t[1].t[0].t[1] << t[0].t[1].t[0] << endl;

    return 0;
}

Wir haben also ein Array str, das ein Array von enthält sct.

Beginnen wir mit letzterem. Sie initialisieren ein Array von sctmit so etwas:

sct x[2] = { {0, 1}, {2, 3} };

Nun könnte für eine einzelne Instanz von strIhnen mitgehen

str y = { { {0, 2}, {4, 6} } };

Was bleibt, str t[2]ist, zwei Kopien der strInitialisierungsausdrücke in geschweiften Klammern anzuordnen :

str t[2] = { { { {0, 2}, {4, 6} } }, { { {1, 3}, {5, 7} } } };

Bearbeitet: Bei der ersten Lesung habe ich die Frage falsch verstanden. Nachdem der Beitrag aktualisiert wurde, wurde klar, dass die Frage lautet, warum es möglich ist, zwei Klammern zu verlieren, aber nur ein Paar zu verlieren, führt zu einem Syntaxfehler.

Um zu verstehen, wie der Parser den Code interpretiert, möchten Sie möglicherweise den Analysebaum betrachten. Sie können gcc-Dump-Bäume in mehreren Phasen des Parsers mit -fdump-tree-...Optionen erstellen. Hier -fdump-tree-originalkann nützlich sein.

Um zusätzliche Verwirrung zu vermeiden, stellen wir sicher, dass die Elemente der Strukturen unterschiedliche Namen haben:

struct sct
{
    int a[2];
};

struct str
{
    sct b[2];
};

Hier ist die Ausgabe, die ich mit GCC 7.5 erhalten habe

>>>> CODE:
str t[2] = { { 0, 2, 4, 6 }, { 1, 3, 5, 7 } };
>>>> tree enabled by -tree-original
struct str t[2] = {{.b={{.a={0, 2}}, {.a={4, 6}}}}, {.b={{.a={1, 3}}, {.a={5, 7}}}}};

Sie können sehen, dass der Compiler implizite Klammern um die Initialisierungsausdrücke für jede Struktur und um die Initialisierungsausdrücke für jedes benannte Feld hinzufügt.

Betrachten Sie nun den Ausdruck, der nicht kompiliert werden kann:

str t[2] = {  { {0, 2},{4, 6} }, { {1, 3},{5, 7} }   };

A die obere Ebene wäre der Baum für diesen Ausdruck

/*Step 1: */ struct str t[2] = { {.b={0, 2}, {4, 6} }, {.b={1, 3}, {5, 7} } };

Aber da b ein Array von ist sct, versuchen wir, dies mit {0,2}get zu initialisieren

sct b[2] = {0, 2};

Dies erweitert sich auf

struct sct b[2] = {{.a={0, 2} }};

Dies ist in C ++ gültig, da das erste Element des Arrays explizit und das zweite Element implizit mit Nullen initialisiert wird.

Mit diesem Wissen erhalten wir den folgenden Baum

/*Step 2: */ struct str t[2] = { {.b={{.a={0, 2} }}, {4, 6} }, {.b={{.a={1, 3} }}, {5, 7} } };

Jetzt bleibt uns Folgendes übrig:

 struct str z = { { { {0,2} }, { {0,0} } }, {4, 6} };

Und der Compiler beschwert sich zu Recht:

 error: too many initializers for str

Beachten Sie zur abschließenden Prüfung die folgende Erklärung

 struct sxc
 {
     sct b[2];
     int c[2];
 }

 struct sxc z = { {0,2} , {4, 6} };

Dies kompiliert und führt zu folgender Struktur:

 { .b = { { .a={0,2} }, { .a={0,0} } }, .c={4, 6} }
Dmitri Chubarov
quelle
Ok, aber wie kommt es, dass Sie die Werte einfach durch Komma getrennt aufzählen dürfen str t[2] = { {0, 2, 4, 6}, {1, 3, 5, 7} }. Ich sehe, dass der Compiler sie in der richtigen Reihenfolge annimmt. Wie würden Sie in der letzten Zeile Ihrer Antwort ändern, um zu dieser zu gelangen { {0, 2, 4, 6}, {1, 3, 5, 7} }?
Cătălina Sîrbu
Ich meine, was wäre der Grund, warum Sie einige geschweifte Klammern loswerden können?
Cătălina Sîrbu
1
Sie können die Absicht der Frage @Dimitri falsch verstehen. Dieser Code wird kompiliert und läuft einwandfrei. Ich glaube, sie fragt: "Braucht es keine weiteren Zahnspangen ( { })?"
Nordländer
1
@ CătălinaSîrbu In der Tat stelle ich fest, dass ich die Absicht Ihres ursprünglichen Beitrags verpasst habe. Vielen Dank für die Klarstellung. Ich habe die Antwort mit dem Versuch aktualisiert, die Logik des Compilers zu rekonstruieren.
Dmitri Chubarov
1
@ CătălinaSîrbu Die Punktnotation wird vom Compiler verwendet, um den Analysebaum mit Anmerkungen zu versehen. Um das Lesen zu erleichtern, habe ich sct.tbzw. str.tin sct.abzw. str.bumbenannt. Das ist wo .aund .bkommt von.
Dmitri Chubarov