繁体   English   中英

GCC 中的畸形 C/C++ 多维数组初始化

[英]Malformed C/C++ Multidimensional Array Initialization in GCC

我想我知道填充如何以正确的格式工作,即:

char arr[3][2] = {{1}, {4,5}};

相当于

char arr[3][2] = {1, 0, 4, 5, 0, 0};

而且,为了好玩,我决定在 GCC 上抛出错误的格式,看看它会返回什么。 对于以下代码: char arr[3][3] = {{1,2,3},12,{4,5,6}}; , GCC 返回:

{1, 2, 3, 12, 4, 0, 0, 0, 0}

但我猜它会返回:

{1, 2, 3, 12, 4, 5, 6, 0, 0}

似乎12抛出它导致{4,5,6}的“标量初始化程序中的多余元素”警告,因此56被删除。

用于展平(因为没有更好的术语)这些多维数组初始化的算法是什么?

这是 GCC 所说的:

test.c: In function 'main':
test.c:119:2: warning: braces around scalar initializer
  char arr[3][3] = {{1,2,3},12,{4,5,6}};
  ^~~~
test.c:119:2: note: (near initialization for 'arr[1][1]')
test.c:119:36: warning: excess elements in scalar initializer
  char arr[3][3] = {{1,2,3},12,{4,5,6}};
                                  ^
test.c:119:36: note: (near initialization for 'arr[1][1]')
test.c:119:38: warning: excess elements in scalar initializer
  char arr[3][3] = {{1,2,3},12,{4,5,6}};
                                    ^
test.c:119:38: note: (near initialization for 'arr[1][1]')

C 2018 6.7.9 第 17 到 21 段讨论了如何从大括号括起来的列表中初始化聚合。 17 说:

每个大括号括起来的初始化器列表都有一个关联的当前 object 当没有指定时,当前 object 的子对象根据当前 object 的类型按顺序初始化:数组元素按递增下标顺序,结构成员按声明顺序,以及联合的第一个命名成员。 相反,指定会导致以下初始化程序开始初始化指定符所描述的子对象。 然后初始化按顺序继续向前,从指示符描述的子对象之后的下一个子对象开始。

20 告诉我们包含的 arrays:

如果聚合或联合包含属于聚合或联合的元素或成员,则这些规则递归地应用于子聚合或包含的联合。 如果子聚合或包含联合的初始化程序以左大括号开头,则由该大括号及其匹配的右大括号括起来的初始化程序初始化子聚合或包含联合的元素或成员。 否则,仅从列表中获取足够多的初始化器来考虑子聚合的元素或成员或包含的联合的第一个成员; 剩下的任何初始化器都将用于初始化当前子聚合或包含的联合所属的聚合的下一个元素或成员。

21 告诉我们缺少初始化程序:

如果大括号括起来的列表中的初始值设定项少于聚合的元素或成员,或者用于初始化已知大小数组的字符串文字中的字符少于数组中的元素,则聚合的其余部分应隐式初始化与具有 static 存储持续时间的对象相同。

现在我们可以看到char arr[3][3] = {{1,2,3},12,{4,5,6}}; 被处理。

首先,我们正在初始化arr ,因此,在第 17 段中,我们将按顺序初始化它的三个元素arr[0]arr[1]arr[2] 由于列表{1,2,3} , 12 , {4,5,6}中的第一项是大括号括起来的列表,第 20 段告诉我们该列表中的项目{1,2,3}将用于初始化子聚合arr[0]

因此arr[0][0]arr[0][1]arr[0][2]被初始化为 1、2 和 3。

接下来,我们考虑arr[1] arr[1]的初始化程序以12开头,而不是左大括号,因此我们不会从大括号封闭的列表中获取初始化程序。 所以我们在列表中剩下两个项目, 12{4,5,6} ,用于初始化arr[1][0]arr[1][1]arr[1][2] (和,之后是arr[2]的元素)。

所以12初始化arr[1][0] ,当然会产生 12。 然后{4,5,6}初始化arr[1][1]

这里有两个问题。 一是第 20 段中的规则说,如果我们用大括号括起来的列表初始化子聚合,则列表中的初始化器用于子聚合的元素或成员。 但是我们正在尝试初始化arr[1][1] ,它是一个标量 object,而不是聚合。 所以规则并没有告诉我们该怎么做。 我相信行为没有定义。

此外,如果我们确实将这个用大括号括起来的列表提供初始值,那么它的值太多了,这也可能是未定义的,因为第 2 段说:

任何初始化程序都不应尝试为未包含在正在初始化的实体中的 object 提供值。

尽管缺少定义,GCC 似乎已将大括号括起来的列表的第一项用于初始化arr[1][1]并丢弃了 rest。 这似乎是合理的。 相比之下,取56来初始化其他数组元素似乎不太合理:由于 C 标准规定的处理,整个大括号括起来的列表已经与arr[1][1]相关联,没有理由从该列表中获取值以用于其他数组元素。

假设编译器在未定义行为之后继续,则arr[1][2]arr[2]的元素都没有初始化程序。 因此,第 21 段中关于缺少初始化程序的规则适用:它们被初始化为好像对于 static object,对于char ,意味着它们被初始化为零。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM