为什么需要多维arrays中更高尺寸的尺寸范围?

根据post,

将2D数组传递给C ++函数

int array[10][10]; void passFunc(int a[][10]) // <---Notice 10 here { // ... } passFunc(array); 

为什么编译器内部观点需要更高的维度。

另一种解释(数组到指针衰减):

假设我们有一维数组,我们就像这样使用它:

 int array[10]; int i = array[3]; 

编译器必须知道在哪里找到array[3] 。 它知道它需要跳过3个int才能到达array[3]那个array[3] 。 所以它有效。

但是如果我们有一个二维数组,

 int array[2][5]; int i = array[1][1]; 

为了让i在这里,编译器需要跳过多少个int ? 它需要跳过整行,再加上一行。 跳过一个很容易,因为我们知道一个int的大小。 但是我们还需要知道数组中行的大小 – 行的大小由类型的大小*每行的列数决定。 这是查看它的一种方式,这解释了为什么需要后一维度。

让我们进一步将其作为一个小的脑筋急转弯

 int array[2][2][2]; int i = array[1][1][1]; 

我们称之为尺寸X,Y,Z

在这里,我们可以说我们有一个有限的int空间。 单位当然是一个int的大小。 行数由Y定义,平面数由Z定义。 这就是X作为基本单位,正如我们所说的那样,它是一个int的大小。 这三者的结合产生了一个“点”。

为了能够到达3D空间中的任何点,我们需要知道每个维度“停止”的位置以及下一个维度的开始位置。 所以我们需要:

  1. 单位( int )的大小,遍历X维度
  2. 每个平面( Y )的大小,以遍历Y维度
  3. 遍历Z维度的平面数量

所以, X已经给了我们,因为我们正在使用int 。 但我们不知道每架飞机的大小,也不知道有多少架飞机。 所以我们需要指定除第一维之外的所有维度。 这是一般规则。

这也解释了为什么这个问题比仅仅指针衰减更精细的解释,因为一旦你达到2个以上的维度,你仍然需要知道它是如何工作的。 换句话说,您需要整体大小(维度的乘积)不溢出,并且您需要每个大小的维度能够使用连续的[]索引。

C / C ++中的数组是一种类型,但不是第一类对象,它们在传递给函数时“衰减”为指向第一个元素的指针。

int[10][10]是一个包含10个int[10]数组的数组……函数声明:

 void foo(int x[][10]); typedef int IntArray10[10]; void bar(IntArray10 *x); 

对于编译器是相同的。

将2d数组传递给函数时,您将传递指向第一个元素的指针(并忽略第一个维度),但元素本身是一个数组,编译器需要知道它的大小。

与您在参数int a[][10]的“[]”所能想到的相反,该函数不采用二维数组而是指向一维数组的指针 – 其原型相当于

 void passFunc(int (*a)[10]) 

array可以像所有数组一样衰减成指向其第一个元素的指针。
像往常一样,该指针是&array[0] ,在这种情况下,它是一个指向带有十个int的数组的指针 – 一个int (*)[10]

所以并不是你需要指定“更高维度”,而是参数是指向十个元素的数组而不是二维数组的指针。

(你不能指向一个未指定大小的数组,因为编译器需要知道a[1]相对于a[0] ,即它需要知道sizeof(*a) 。)

它是必需的,因为在C中不存在多维数组的概念。
我们可以定义任何数组,甚至是另一个数组。 最后一个可以看作是一个多维数组。
现在考虑将二维数组视为:

 int arrray[5][4]; 

这将被解释为一个包含5个元素的数组,每个元素都是4个int的数组。
在内存中,布局是顺序的:首先是第一个数组的4个元素,然后是第二个元素,依此类推,直到第4个元素的第5个数组。
要访问第3个数组array[2][1]的第2个元素,编译器必须跳过前2个数组, 但这样做需要知道要跳过多少个元素
即数学上第三个数组的第二个元素的地址是:

 //pElement2Array3 points the 2nd element of the 3rd array. //Note that 4 is the number of elements of the base dimension int *pElement2Array3 = &array[0] + (2 * 4) + 1; 

(注意:术语(2 * 4) + 1不会乘以sizeof(int)因为这是通过指针算法自动完成的。)
上面的数学运算由编译器在使用寻址array[2][1]时自动执行, 但要执行它,编译器必须知道基数组中有多少个元素(可能是数组数组的数组)。 。)

简单地说,因为多维数组从右到左“增长”。 想想这样:

int arr [a];a整数数组。

int arr [b][a]a整数的b数组的数组。

等等。

至于为什么在将数组传递给函数时可以省略最左边的维度,这是因为“数组衰减”。 6.7.6.3/7说:

将参数声明为”类型数组”应调整为”限定指向类型”

也就是说,如果将一个函数参数声明为int param[10]int param[] ,它将被编译器静默/不可见地替换为指向int: int* param的指针,该指针指向数组的第一个元素。 数组本身仍然由调用者分配。

它的工作原理是防止数组按值传递给函数,这将是非常无效的,并且在大多数情况下没有任何意义。

现在对于多维数组的情况,适用上述相同的规则。 所以,如果你有int param[10][10]它会衰变成指向第一个元素的指针。 第一个元素是一个包含10个整数的数组,因此你得到一个指向 10个整数数组的数组指针int (*param)[10]

如果你有int param[][10]会发生同样的情况,你仍然会得到一个int (*param)[10] 。 所以最左边的维度可以是任何东西 – 它无关紧要,因为它无论如何都会被忽略。

但是最左边的其他维度是必需的,否则编译器将不知道数组将衰减到哪个指针类型。 在函数声明的特殊情况之外,没有int(*param)[]这样的东西,这意味着“指向未知大小的数组的数组指针”。

作为函数参数int a[][10]等价于int (*a)[10] ,意思是: a是指向10 int数组的指针 。 在这种情况下,它不代表2Darrays。

如果较高的维度留空,则编译器不可能知道指针a指向的数组的长度,并且不能执行指针运算。