【问题标题】:Why do I not need 3 levels of braces for initializing 3 levels of arrays?为什么我不需要 3 级大括号来初始化 3 级数组?
【发布时间】:2020-02-08 08:42:42
【问题描述】:

我遇到了这个例子

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'

谁能解释一下原因?

这是一张图片来说明我的看法:

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

【问题讨论】:

  • 在询问涉及构建错误的问题时,请始终包括您得到的实际错误,并以文本的形式完整完整地复制粘贴。

标签: c++ struct nested uniform-initialization


【解决方案1】:

这看起来像一个简单的错字,但情况很复杂,可以逐步解决。

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

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

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

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

【讨论】:

  • 好的,但是如何允许您简单地枚举以逗号分隔的值作为str t[2] = { {0, 2, 4, 6}, {1, 3, 5, 7} }。我看到编译器按顺序假定它们。从您回答的最后一行开始,您将如何修改以得到这个{ {0, 2, 4, 6}, {1, 3, 5, 7} }
  • 我的意思是你可以去掉一些大括号的原因是什么?
  • 您可能误解了@Dimitri 问题的意图。此代码编译并运行良好。我想她是在问“不需要另一套牙套 ({ }) 吗?”
  • @CătălinaSîrbu 确实我意识到我错过了您原始帖子的意图,感谢您的澄清。我已经更新了答案,试图重构编译器的逻辑。
  • @CătălinaSîrbu 编译器使用点表示法来注释分析树。为了便于阅读,我将sct.tstr.t 分别重命名为sct.astr.b。这就是.a.b 的来源。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 2016-04-14
  • 1970-01-01
  • 2017-06-30
  • 1970-01-01
  • 1970-01-01
  • 2018-03-06
相关资源
最近更新 更多