3レベルの配列を初期化するのに3レベルのブレースが必要ないのはなぜですか?


8

私はこの例に出くわしました

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;
}

これはコンパイルして正常に動作します。それは出力を与えます34

初期化の構文は次のようになるはずです。

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

の代わりに

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

しかし、これは与えました:

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

誰かが理由を説明できますか?

これは私がこれを見る方法を説明するための写真です: ここに画像の説明を入力してください

ネストされた構造体の初期化に関してはどうすればよいですか?


3
ビルドエラーに関する質問をする場合は、必ず、実際に発生したエラーを含めてください。コピーして貼り付けたテキストは、完全かつ完全なものにしてください。
プログラマー

回答:


7

これは単純なタイプミスのように見えますが、状況は段階的にそれに取り組むのに十分複雑です。

最初に、うまくいくと思われるソリューションを示します。

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;
}

したがって、の配列strを保持するの配列がありsctます。

後者から始めましょう。sct次のようなもので配列を初期化します:

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

今のstrあなたの単一のインスタンスのために行くことができます

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

残っているのstr t[2]は、str中括弧内に初期化式の2つのコピーを配置することです。

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

編集:私は最初の読書で質問を誤解しました。投稿が更新された後、2組のブレースを削除することが可能であるのに、1組だけを削除すると構文エラーが発生する理由が問題であることが明らかになりました。

パーサーがコードをどのように解釈しているかを理解するには、解析ツリーを調べてください。-fdump-tree-...オプションを使用して、パーサーのいくつかの段階でgccダンプツリーを作成できます。ここ-fdump-tree-originalが役に立つかもしれません。

追加の混乱を避けるために、構造体の要素が異なる名前であることを確認しましょう:

struct sct
{
    int a[2];
};

struct str
{
    sct b[2];
};

これは私がGCC 7.5で得た出力です

>>>> 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}}}}};

コンパイラーは、各構造の初期化式と各名前付きフィールドの初期化式の周りに暗黙の括弧を追加することがわかります。

次に、コンパイルに失敗した式について考えます。

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

この式のツリーの上位レベルは

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

bが配列の場合でもsct、私たちはとそれを初期化しようとする{0,2}取得します

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

これは

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

配列の最初の要素は明示的に初期化され、2番目の要素は暗黙的にゼロで初期化されるため、これは有効なC ++です。

この知識により、次のツリーが得られます

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

これで、次のことができます。

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

そしてコンパイラは当然文句を言う:

 error: too many initializers for str

最終チェックとして、次の宣言を検討してください

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

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

これはコンパイルされ、次の構造になります。

 { .b = { { .a={0,2} }, { .a={0,0} } }, .c={4, 6} }

[OK]を、どのようにそれはあなたが単に列挙としてコンマで値を分離することが許可されていることを来ませんstr t[2] = { {0, 2, 4, 6}, {1, 3, 5, 7} }。コンパイラがそれらを順番に仮定しているのがわかります。あなたの答えの最後の行から、これに到達するためにどのように変更します{ {0, 2, 4, 6}, {1, 3, 5, 7} }か?
カタリナSIRBU

中かっこを取り除くことができる理由は何ですか?
カタリナSIRBU

1
@Dimitriの質問の意図を誤解しているかもしれません。このコードはコンパイルして正常に動作します。彼女は「別の中括弧({ })が必要ではないか?」
ノーザン

1
@CătălinaSîrbu確かに私は元の投稿の意図を逃したことを理解しています。説明をありがとうございます。コンパイラーのロジックを再構築する試みで、答えを更新しました。
ドミトリチュバロフ

1
@CătălinaSîrbuドット表記は、解析ツリーに注釈を付けるためにコンパイラーによって使用されます。読み物事を簡単にするために、私は名前を変更したsct.tstr.tするsct.astr.b、それぞれ。それがどこ.aから.b来たのかです。
ドミトリチュバロフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.