为什么我不需要 3 级大括号来初始化 3 级数组?

Why do I not need 3 levels of braces for initializing 3 levels of arrays?

我遇到了这个例子

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'

有人可以解释为什么吗?

这张图片说明了我的看法:

在初始化嵌套结构时我应该怎么想?

这看起来是一个简单的错字,但情况很复杂,需要逐步解决。

首先让我展示一下似乎有效的解决方案:

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初始化表达式:

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

已编辑: 在第一次阅读时我误解了这个问题。 post 更新后,很明显问题是为什么可以去掉两对大括号,但只去掉一对会导致语法错误。

要了解解析器如何解释代码,您可能希望查看解析树。您可以使用 -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} }};

这是有效的 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} }