静态数组:
你似乎没有完全明白这一点。我想试着解释一下。正如上面的一些答案所描述的,C++ 中的2D Array 作为1D Array 存储在内存中。
int arr[3][4] ; //consider numbers starting from zero are stored in it
在内存中看起来有点像这样。
1000 //ignore this for some moments 1011
^ ^
^ ^
0 1 2 3 4 5 6 7 8 9 10 11
|------------| |-----------| |-------------|
First Array Second Array Third Array
|----------------------------------------------|
Larger 2D Array
考虑这里,更大的2D Array 存储为连续的内存单元。它由12 个元素组成,从0 到11。行是3,列是4。如果要访问第三个数组,则需要跳过整个第一个和第二个数组。也就是说,您需要跳过等于cols 的数量乘以要跳过的数组数量的元素。结果是cols * 2。
现在,当您指定要访问数组的任何单个索引的维度时,您需要事先告诉编译器确切要跳过多少元素。所以你给它cols 的确切数字来执行其余的计算。
那么它是如何进行计算的呢?假设它适用于column major order,也就是说,它需要知道要跳过的列数。当您将此数组的一个元素指定为...
arr[i][j] ;
编译器自动执行此计算。
Base Address + (i * cols + j) ;
让我们尝试一个指数的公式来测试它的准确性。我们想要访问2nd 数组的3rd 元素。我们会这样做......
arr[1][2] ; //access third element of second array
我们把它放在公式中......
1000 + ( 1 * 4 + 2 )
= 1000 + ( 6 )
= 1006 //destination address
我们到达6 所在的地址1006。
简而言之,我们需要告诉编译器cols 的数量以进行此计算。所以我们把它作为一个函数的参数发送。
如果我们正在处理3D Array,像这样...
int arr[ROWS][COLS][HEIGHT] ;
我们必须在函数中将数组的最后两个维度发送给它。
void myFunction (int arr[][COLS][HEIGHT]) ;
现在的公式会变成这样..
Base Address + ( (i * cols * height) + (j * height) + k ) ;
要这样访问它...
arr[i][j][k] ;
COLS 告诉编译器跳过2D Array 的数量,HEIGHT 告诉它跳过1D Arrays 的数量。
对于任何维度,依此类推。
动态数组:
当您询问在这样声明的动态数组的情况下的不同行为时..
int ** arr ;
编译器对它们的处理方式不同,因为Dynamic 2D Array 的每个索引都包含另一个1D Array 的地址。它们可能出现在堆上的连续位置上,也可能不出现。它们的元素由它们各自的指针访问。我们上面的静态数组的动态对应物看起来有点像这样。
1000 //2D Pointer
^
^
2000 2001 2002
^ ^ ^
^ ^ ^
0 4 8
1 5 9
2 6 10
3 7 11
1st ptr 2nd ptr 3rd ptr
假设是这种情况。这里是2D Pointer 或1000 位置上的数组。它将地址保存到2000,它本身保存了一个内存位置的地址。这里的指针运算是由编译器完成的,通过它来判断元素的正确位置。
为了给2D Pointer分配内存,我们这样做了..
arr = new int *[3] ;
并以这种方式为每个索引指针分配内存..
for (auto i = 0 ; i < 3 ; ++i)
arr[i] = new int [4] ;
最后,2D Array 中的每个ptr 本身就是一个数组。要访问您所做的元素...
arr[i][j] ;
编译器会这样做...
*( *(arr + i) + j ) ;
|---------|
1st step
|------------------|
2nd step
在第一步中,2D Array 被取消引用到其适当的1D Array,在第二步中,1D Array 被取消引用以到达适当的索引。
这就是为什么 Dynamic 2D Arrays 被发送到函数而不提及它们的行或列的原因。
注意:
很多细节都被忽略了,很多东西都在描述中,特别是内存映射只是为了给你一个想法。