初始化C/C 多维阵列时省略尺寸

Omitting Sizes while Initializing C/C++ Multidimensional Arrays

本文关键字:时省 初始化 阵列      更新时间:2023-10-16

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

这意味着 忽略牙套。到现在为止,我已经在三个不同的编译器上尝试过。

我希望答案是"这只是编译器的依赖性"。我无法访问标准,因此请从标准提供答案。我不需要肠感,我有我的。

以下是从" c编程语言"的A8.7节中由K& r,第二版,第219,220页:

聚合是一个结构或数组。如果一个骨料包含汇总类型的成员,初始化规则递归适用。牙套可以在初始化中省略如下:如果汇总成员的初始化器本身是一个汇总从左支架开始,然后是随后的逗号分隔列表初始化器初始化子骨料的成员;这是错误的初始化器比成员更多。如果,但是,亚凝集酸盐的初始化器不从左开始支撑,然后仅列出列表中的足够元素来考虑亚参议员的成员;其余成员将留给初始化亚群落的下一个骨料的成员是一部分。例如,

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

由于未指定尺寸和

有三个初始化器。

因此,给定此行

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

编译器将递归初始化数组,并指出每个子阵列以左支架开头,并且不超过所需的初始化数量,并且会计算确定数组的第一个维度的子阵列数量。

以下来自" C编程语言"的A8.7节。由K& r,第二版,第220页:

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

是一个完全包装的初始化:135初始化数组y[0]的第一行,即y[0][0]y[0][1]y[0][2]。同样,接下来的两行初始化y[1]y[2]。初始化器早期结束,因此用0初始化了y[3]的元素。

可以实现相同的效果
float y[4][3] = {
   1, 3, 5, 2, 4, 6, 3, 5, 7 
};

请注意,在这两种情况下,数组的第四行都将初始化零,由于指定了不够的初始化器。

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

初始化y的第一列,然后离开其余的0

因此,编译器不会忽略内括号。但是,如果您指定所有初始化器,则内括号是可选的。使用内括号可以使您对初始化有更多的控制.7 of K& r的" C编程语言",第二版,第220页

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如果汇总或联合包含的要素或成员 聚合或工会,这些规则递归适用于 亚群落或包含工会。如果是 亚凝集或包含的联盟始于左支架, 该支架及其匹配的右支架所包围的初始化器 初始化子聚集元的元素或成员 包含工会。否则,列表中只有足够的初始化器是 用于考虑子聚集的要素或成员或 包括联盟的第一任成员;剩余的任何初始化器 留下来初始化下一个元素或骨料的成员 当前的子聚集或包含联合是一个部分。

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[1]y[2]的。另外,

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

根据指定的z的第一列,用zeros 。相关:

8.5.1聚合

(...)

初始化多维阵列时,初始化器 - 扣除元素用阵列的最后(最右)索引初始化元素,以变化最快(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如果初始化器列表以左括号开头,那么随后的逗号分隔列表的初始化器 - 序列列表初始化了子聚集体的成员;与成员相比,有更多的初始化器扣除是错误的。但是,如果子聚集材料的初始化列表不是从左括号开始的,则仅列出列表中的初始用量扣除以初始化subggregate的成员。剩余的任何初始化器段都可以初始化当前子聚集物为成员的骨料的下一个成员。[示例:

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()的表达式(即用0.0初始初始化)显式初始初始初始初始化。在下面的示例中,初始化列表中的牙套被省略;但是,初始化列表具有与上述示例的完全支撑的初始列表相同的效果,

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

y的初始化器以左支架开头,但是y [0]的初始化器不使用,因此使用了列表中的三个元素。同样,接下来的三个是针对y[1]y[2]的。 - 末尾示例]

语言规范确实指出,在数组初始化中,"未给出的字段"将为零:

第8.5节:C 的N3337版本的Paragpraph 7标准:

如果列表中的初始化器段少于 聚合中的成员,然后每个成员未明确初始化 应从空的初始化器列表(8.5.4)初始化。[ 示例:

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

用1个ss.a初始化为ss.a,ss.b,带有" asdf",ss.c的值 形式int()的表达,即0。-结束示例]