如果明确给出多维数组,为什么char [] [] = {{...},{...}}不可能?

esg*_*dir 24 c c++ compile-time multidimensional-array

我仔细阅读了这篇文章.我理解解释的规则,但我想知道在定义一个常量多维数组并使用给定类型的已知值直接初始化时,究竟是什么阻止编译器接受以下语法:

const int multi_arr1[][] = {{1,2,3}, {1,2,3}}; // why not?
const int multi_arr2[][3] = {{1,2,3}, {1,2,3}}; // OK

error: declaration of 'multi_arr1' as multidimensional array must have bounds
       for all dimensions except the first
Run Code Online (Sandbox Code Playgroud)

什么阻止编译器向右看并意识到我们正在处理每个"子阵列"的3个元素,或者只有在程序员为每个子阵列传递例如不同数量的元素时才返回错误{1,2,3}, {1,2,3,4}

例如,当处理1D char数组时,编译器可以查看右侧的字符串=,这是有效的:

const char str[] = "Str";
Run Code Online (Sandbox Code Playgroud)

我想了解发生了什么,以便编译器无法推断出数组维度并计算分配大小,因为现在我觉得编译器已经拥有了所需的所有信息.我在这里错过了什么?

Eri*_*hil 27

要求编译器从初始化器推断内部维度将要求编译器以标准避免的方式追溯工作.

该标准允许初始化的对象引用自己.例如:

struct foo { struct foo *next; int value; } head = { &head, 0 };
Run Code Online (Sandbox Code Playgroud)

这定义了最初指向自身的链表的节点.(据推测,稍后会插入更多节点.)这是有效的,因为C 2011 [N1570] 6.2.1 7表示标识符head"具有在其声明符完成后才开始的范围." 声明符是语法的一部分.一个声明,包括标识符名称以及声明的数组,函数和/或指针部分(例如,f(int, float)并且*a[3]是声明中的声明符,如float f(int, float)int *a[3]).

由于6.2.1 7,程序员可以编写这个定义:

void *p[][1] = { { p[1] }, { p[0] } };
Run Code Online (Sandbox Code Playgroud)

考虑初始化器p[1].这是一个数组,因此它会自动转换为指向其第一个元素的指针p[1][0].编译器知道该地址,因为它知道p[i]的数组为1 void *(对于任何值i).如果编译器不知道有多大p[i],则无法计算此地址.所以,如果C标准允许我们写:

void *p[][] = { { p[1] }, { p[0] } };
Run Code Online (Sandbox Code Playgroud)

然后编译器必须继续扫描过去,p[1]这样它就可以计算为第二个维度给出的初始值设定项的数量(在这种情况下只有一个,但是我们必须至少扫描一下}才能看到它,它可能会更多),然后回去计算值p[1].

该标准避免了强制编译器执行此类多次传递工作.要求编译器推断内部维度会违反此目标,因此标准不会这样做.

(事实上​​,我认为标准可能不需要编译器做任何超过有限量的前瞻,可能只是在标记化过程中只有几个字符和解析语法时的单个标记,但我不确定.有些事情在链接时间之前不知道值,例如void (*p)(void) = &SomeFunction;,链接器填充这些值.)

  • 顺便说一句,C99允许类似:`int*q [5] = {(int []){1,2,3,-1},(int []){1,2,-1},(int []){1,2,3,4,5,6,7,-1}};`.语法有点尴尬,代码需要使用`(int*)[]`而不是二维数组,并且没有很好的方法来找出内部维度,除非它被暗示数据[例如,通过在每行的末尾包括哨兵],但如果行具有不同数量的初始化器,则该方法可能比尝试使用二维数组更有效. (2认同)

Arm*_*yan 7

在实现编译器时没有什么不可能在初始化器存在的情况下推导出多维数组的最内层维度,但是,它是C或C++标准不支持的功能,显然,对此没有太大的需求.功能打扰.

换句话说,标准语言不支持您所追求的内容.如果有足够的人需要它,它可以得到支持.他们没有.