在初始化C / C ++多维数组时省略大小

我对C / C ++编译器的了解是,它们在初始化多维数组时忽略了内部括号。

所以,你不能这样做:

int myArray[][] = { { 2, 3 }, { 4, 5 }, { 4, 1 } }; 

因为编译器会完全看到它

 int myArray[][] = { 2, 3, 4, 5, 4, 1 }; 

现在它不知道它是6 * 1,3 * 2,2 * 3,1 * 6,还是其他东西(因为这可能是部分初始化列表,不一定完整)。

我的问题是,为什么这在许多编译器中都有效?

 int myArray[][2] = { { 2 }, { 4, 5 }, { 4, 1 } }; 

编译器“直观地”将其视为:

 int myArray[][2] = { { 2, 0 }, { 4, 5 }, { 4, 1 } }; 

这意味着它不会忽略大括号。 到目前为止,我已经尝试过三种不同的编译器,并且都工作了。

我希望答案是“这只是编译器依赖”。 我无法访问该标准,因此请提供标准的答案。 我不需要直觉,我有我的。

尝试2。

以下内容来自K&R的“The C Programming Language”第A8.7节,第2版,第219,220页

聚合是结构或数组。 如果聚合包含聚合类型的成员,则初始化规则将递归应用。 可以在初始化中省略大括号,如下所示:如果聚合的成员的初始化程序本身是聚合,以左括号开头,则后续的逗号分隔的初始化程序列表初始化子聚合的成员; 如果有比成员更多的初始化者是错误的。 但是,如果子集合的初始化程序不以左括号开头,那么只有列表中的足够元素才能考虑子集合的成员; 任何剩余的成员都要初始化子聚合所属的聚合的下一个成员。 例如,

  int x[] = { 1, 3, 5 }; 

声明并将x初始化为具有成员的1维数组,因为没有指定大小并且有三个初始值设定项。

因此,给定这一行

 int myArray[][2] = { { 2 }, { 4, 5 }, { 4, 1 } }; 

编译器将递归地初始化数组,注意每个子数组以左括号开始并且不超过所需数量的初始化器,并将计算子数组的数量以确定数组的第一个维度。

以下内容来自K&R的“The C Programming Language”第A8.7节,第2版,第220页

 float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 } }; is equivalent to float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 }; Note that in both cases, the fourth row of the array will be initialized with zero, since not enough initializers were specified. float y[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } }; initializes the first column of y and leaves the rest 0. 

因此编译器不会忽略内部大括号。 但是,如果按顺序指定所有初始化程序而没有间隙,则内括号是可选的。 如果您不想指定一组完整的初始值设定项,则使用内括号可以更好地控制初始化。

以下内容来自K&R的第2版,第220页的“The C Programming Language”的A8.7

 float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 } }; 

相当于

 float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 }; 

请注意,在这两种情况下,数组的第四行将初始化为零,因为没有指定足够的初始值设定项。

 float y[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } }; 

初始化y的第一列,剩下0。

因此编译器不会忽略内部大括号。 但是,如果按顺序指定所有初始化程序而没有间隙,则不需要内括号。 如果您不想指定一整套初始化程序,则使用内括号可以更好地控制初始化。

以下是C标准中的一些引用,可以帮助理解数组的初始化。

20如果聚合或联合包含聚合或联合的元素或成员,则这些规则将递归地应用于子聚合或包含的联合。 如果子集合或包含的并集的初始值设定项以左括号开头,则由该括号括起的初始值设定项及其匹配的右括号初始化子集合或所包含的并集的元素或成员。 否则,只有列表中的足够的初始值设定项用于说明子集合的元素或成员或所包含的并集的第一个成员; 任何剩余的初始值设定项都用于初始化当前子聚合或包含的union所属的聚合的下一个元素或成员。

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

22如果初始化未知大小的数组,则其大小由具有显式初始值设定项的最大索引元素确定。 数组类型在其初始化列表的末尾完成。

这是标准的一个例子

 int y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, }; 

是一个完全括号初始化的定义:1,3和5初始化y的第一行(数组对象y [0]),即y [0] [0],y [0] [1]和y [0] [2]。 同样,接下来的两行初始化y [1]和y [2]。 初始化器提前结束,因此y [3]用零初始化。 准确地说,可以达到同样的效果

 int y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 }; 

y [0]的初始值设定项不以左括号开头,因此使用列表中的三个项目。 同样地,接下来的三个连续采用y [1]和y [2]。

ANSCI C-89( 3.5.7 )说:

 float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, }; 

是一个完全括号初始化的定义:1,3和5初始化数组对象y[0]的第一行(即y[0][0]y[0][1]y[0][2] )。 同样,接下来的两行初始化y[1]y[2] 。 初始化器提前结束,因此y[3]用零初始化。 准确地说,可以达到同样的效果

 float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 }; 

y[0]的初始y[0]不以左括号开头,因此使用列表中的三个项目。 同样,接下来的三个连续采用y[1]y[2] 。 也,

 float z[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } }; 

按指定初始化 z的第一列,并使用零初始化其余列。

我想,这一章是相关的:

8.5.1聚合

(……)

初始化多维数组时,initializer-clauses初始化元素,最后(最右边)的数组索引变化最快(8.3.4)。 [例如:

 int x[2][2] = { 3, 1, 4, 2 }; 

初始化x[0][0]为3, x[0][1]为1, x[1][0]为4, x[1][1]为2.另一方面,

 float y[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } }; 

初始化y的第一列(被视为二维数组)并将其余部分保留为零。 – 示例]

在表格的声明中

 T x = { a }; 

大括号可以在初始化列表中被删除,如下所述.105如果初始化列表以左括号开头,则后续的逗号分隔的初始化子句列表初始化子聚合的成员; 如果有比成员更多的初始化子条款是错误的。 但是,如果子集合的初始化列表不以左括号开头,则只从列表中获取足够的初始化子句来初始化子集合的成员; 剩下的任何初始化子句用于初始化当前子聚合为成员的聚合的下一个成员。 [例如:

 float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, }; 

是一个完全支撑的初始化:1,3和5初始化数组y[0]的第一行,即y[0][0]y[0][1]y[0][2] 。 同样,接下来的两行初始化y[1]y[2] 。 初始化器提前结束,因此y[3] s元素被初始化,就像用float()forms的表达式显式初始化一样,即用0.0初始化。 在以下示例中,初始化列表中的大括号被省略; 但是初始化列表与上面示例的完全支撑的初始化列表具有相同的效果,

 float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 }; 

y的初始化程序以左括号开头,但y [0]的初始化程序不是,因此使用列表中的三个元素。 同样地,接下来的三个连续采用y[1]y[2] 。 – 示例]

语言规范确实在数组初始化中声明“未给出的字段将为零”:

第8.5节:n3337版本的C ++ 11标准的paragpraph 7:

如果列表中的initializer-clause少于聚合中的成员,那么未显式初始化的每个成员都应从空的初始化列表(8.5.4)初始化。 [例如:

 struct S { int a; const char* b; int c; }; S ss = { 1, "asdf" }; 

使用“asdf”初始化ss.a,使用“asdf”初始化ss.b,使用int()forms的表达式的值初始化ss.c,即 – 结束示例]