初始化 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 } };

这意味着它不会忽略大括号.到目前为止,我已经在三种不同的编译器上进行了尝试,并且都可以正常工作.

我希望答案是这只是依赖于编译器".我无权访问该标准,因此请提供来自该标准的答案.我不需要直觉,我有我的.

解决方案

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

<块引用>

聚合是一个结构或数组.如果聚合包含聚合类型的成员,初始化规则递归应用.初始化中可以省略大括号,如下所示:如果本身是聚合的聚合成员的初始化程序以左大括号开头,然后是后续的逗号分隔列表初始化器初始化子聚合的成员;它是初始化器比成员多是错误的.如果,但是,子聚合的初始值设定项不以 left 开头大括号,则只考虑列表中的足够元素子集合的成员;任何剩余的成员都留给初始化子聚合的聚合的下一个成员是一部分.例如,

<块引用>

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

声明并初始化 x 为具有三个成员的一维数组,因为没有指定大小并且

<块引用>

有三个初始化器.

因此,鉴于这一行

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

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

以下内容来自The C Programming Language"的 A8.7 节.作者:K&R,第 2 版,第 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].初始化器提前结束,因此 y[3] 的元素被初始化为 0.完全一样的效果可以通过

<块引用>

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

注意,在这两种情况下,数组的第四行都会被初始化为零,因为没有指定足够的初始化器.

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

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

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

What I know about C/C++ compilers is that they ignore inner braces while initializing multidimensional arrays.

So, you can't do this:

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

because the compiler will see it exactly as

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

and now it doesn't know if it is 6 * 1, 3 * 2, 2 * 3, 1 * 6, or even something else (since this can be a partial initialization list, not necessarily complete).

My question is, why does this work in many compilers?

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

The compiler "intuitively" sees it as:

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

which means it doesn't ignore the braces. I've tried it on three different compilers till now and all worked.

I expect the answer to be "this is just compiler-dependent". I don't have access to the standard, so please provide an answer from the standard. I don't need gut feelings, I have mine.

解决方案

The following is from section A8.7 of "The C Programming Language" by K&R, 2nd edition, pages 219,220:

An aggregate is a structure or array. If an aggregate contains members of aggregate type, the initialization rules apply recursively. Braces may be elided in the initialization as follows: if the initializer for an aggregate's member that is itself an aggregate begins with a left brace, then the succeeding comma-separated list of initializers initialize the members of the sub aggregate; it is erroneous for there to be more initializers than members. If, however, the initializer for a subaggregate does not begin with a left brace, then only enough elements from the list are taken to account of the members of the subaggregate; any remaining members are left to initialize the next member of the aggregate of which the subaggregate is a part. For example,

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

declares and initializes x as a 1-dimensional array with three members, since no size was specified and

there are three initializers.

Therefore, given this line

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

the compiler will recursively initialize the array, noting that each subarray starts with a left brace and has no more than the required number of initializers, and will count the number of subarrays to determine the first dimension of the array.

The following is from section A8.7 of "The C Programming Language" by K&R, 2nd edition, page 220:

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

is a completely-bracketed initialization: 1,3 and 5 initialize the first row of the array y[0], namely y[0][0], y[0][1], and y[0][2]. Likewise the next two lines initialize y[1] and y[2]. The initializer ends early, and therefore the elements of y[3] are initialized with 0. Precisely the same effect could have been achieved by

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.

So the compiler doesn't ignore the inner braces. However, the inner braces are optional if you specify all of the initializers in order with no gaps. Using the inner braces gives you more control over the initialization, if you don't want to specify a full set of initializers.

相关文章