我想我了解填充如何使用正确的格式,即:
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}
的警告,因此5和6将被删除。
用于使这些多维数组初始化变平(因为没有更好的术语)的算法是什么?
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说:
每个包含大括号的初始值设定项列表都有一个关联的当前对象。当没有指定时,当前对象的子对象将根据当前对象的类型按顺序初始化:按下标递增顺序的数组元素、按声明顺序的结构成员以及并集的第一个命名成员。相反,指定会导致下面的初始化器开始初始化由指定器描述的子对象。然后,初始化按顺序继续进行,从指示符所描述的子对象之后的下一个子对象开始。
20告诉我们包含的数组:
如果聚合或联合包含聚合或联合的元素或成员,则这些规则将递归应用于子聚合或包含的联合。如果子聚合或包含联合的初始值设定项以左大括号开头,则该大括号及其匹配的右大括号所包含的初始值设置项将初始化子聚合或所包含联合的元素或成员。否则,只考虑列表中足够的初始值设定项来考虑子聚合的元素或成员或所包含并集的第一个成员;剩下的任何初始化器都可以初始化当前子聚合或包含的并集所属的聚合的下一个元素或成员。
21告诉我们缺少初始化器:
如果大括号括起的列表中的初始化程序少于聚合的元素或成员,或者用于初始化已知大小数组的字符串文字中的字符少于数组中的元素,则聚合的其余部分应隐式初始化为与具有静态存储持续时间的对象相同。
现在我们可以看到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}
中的第一项是brace-enclosed列表,第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。然后CCD_ 31初始化CCD_。
这里有两个问题。一个是,第20段中的规则规定,如果我们用大括号括起的列表初始化子集合,则列表中的初始化器用于子集合的元素或成员。但我们正在尝试初始化arr[1][1]
,它是一个标量对象,而不是聚合。所以规则并没有告诉我们该做什么。我认为行为并没有定义。
此外,如果我们确实将这个括号内的列表视为提供初始值,那么它的值太多了,这也可能是未定义的,因为第2段说:
任何初始化器都不应试图为未包含在正在初始化的实体中的对象提供值。
不管缺乏定义,GCC似乎已经使用了大括号列表的第一项来初始化arr[1][1]
,并丢弃了其余项。这似乎是合理的。相比之下,使用5
和6
来初始化其他数组元素似乎不太合理:由于C标准指定的处理,整个大括号包围的列表都与arr[1][1]
相关联,没有理由从该列表中取值用于其他数组元素。
假设编译器在未定义的行为之后继续,则arr[1][2]
和arr[2]
的元素没有初始化器。因此,第21段中关于缺少初始化器的规则适用:它们被初始化为静态对象,对于char
,这意味着它们被初始化到零。