提问者:小点点

GCC中错误的C/C++多维数组初始化


我想我知道填充是如何在适当的格式下工作的,例如:

字符字符[3][2]={{1},{4,5}};

相当于

字符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被删除。

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

海合会是这么说的:

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]')

共1个答案

匿名用户

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

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

20告诉我们关于包含数组:

如果聚合或联合包含作为聚合或联合的元素或成员,则这些规则递归应用于子聚合或包含的联合。 如果子聚合或包含的联合的初始值设定项以左大括号开头,则由该大括号及其匹配的右大括号括起的初始值设定项将初始化子聚合或包含的联合的元素或成员。 否则,只从列表中取足够的初始值设定项来考虑子聚合的元素或成员或包含的联合的第一个成员; 剩余的初始值设定项将用于初始化聚合的下一个元素或成员,当前子聚合或包含的联合是该元素或成员的一部分。

21告诉我们缺少初始值设定项:

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

现在我们可以看到字符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],它是一个标量对象,而不是聚合对象。 所以规则并没有告诉我们该怎么做。 我相信行为没有定义。

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

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

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

假设编译器在未定义的行为之后继续,则ARR[1][2]没有初始值设定项,ARR[2]的元素也没有。 因此,第21段中关于缺少初始值设定项的规则适用:初始值设定项就像静态对象一样初始化,对于char,这意味着初始值设定为零。

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(gcc|中|c|c++|多维|数组|初始化)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?