为什么不像普通数组那样在函数中声明2D数组参数?
void F(int bar[]){} //Ok
void Fo(int bar[][]) //Not ok
void Foo(int bar[][SIZE]) //Ok
Run Code Online (Sandbox Code Playgroud)
为什么需要声明列的大小?
Cod*_*ash 18
静态数组:
你似乎没有完全明白这一点.我想过尝试解释一下.作为一些上述答案描述中,2D Array在C++被存储在存储器中作为一个1D Array.
int arr[3][4] ; //consider numbers starting from zero are stored in it
Run Code Online (Sandbox Code Playgroud)
在记忆中看起来有点像这样.
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
Run Code Online (Sandbox Code Playgroud)
考虑到这里,Bigger 2D Array存储为连续的内存单元.它由总的12元件,从0到11.行3和列是4.如果要访问第三个数组,则需要跳过整个第一个和第二个数组.也就是说,您需要跳过等于cols您想要跳过的数组所乘的数字的元素.它出来了cols * 2.
现在,当您指定维度以访问数组的任何单个索引时,您需要事先告诉编译器确切要跳过多少元素.所以你给它准确的数量cols来执行其余的计算.
那么它如何执行计算?让我们说它适用于column major order,也就是说,它需要知道要跳过的列数.当您指定此数组的一个元素为...时
arr[i][j] ;
Run Code Online (Sandbox Code Playgroud)
编译器自动执行此计算.
Base Address + (i * cols + j) ;
Run Code Online (Sandbox Code Playgroud)
让我们尝试一个索引的公式来测试它的准确性.我们想要访问Array 的3rd元素2nd.我们会这样做......
arr[1][2] ; //access third element of second array
Run Code Online (Sandbox Code Playgroud)
我们把它放在公式中......
1000 + ( 1 * 4 + 2 )
= 1000 + ( 6 )
= 1006 //destination address
Run Code Online (Sandbox Code Playgroud)
我们到达1006所在的地址6.简而言之,我们需要告诉编译器cols这个计算的数量.所以我们将它作为参数发送到函数中.
如果我们正在努力3D Array,就像这样......
int arr[ROWS][COLS][HEIGHT] ;
Run Code Online (Sandbox Code Playgroud)
我们必须在函数中发送数组的最后两个维度.
void myFunction (int arr[][COLS][HEIGHT]) ;
Run Code Online (Sandbox Code Playgroud)
现在的公式将成为这个..
Base Address + ( (i * cols * height) + (j * height) + k ) ;
Run Code Online (Sandbox Code Playgroud)
像这样访问它...
arr[i][j][k] ;
Run Code Online (Sandbox Code Playgroud)
COLS告诉编译器跳过数字2D Array,并HEIGHT告诉它跳过数字1D Arrays.等等任何方面等等.
动态数组:
当您询问有关因此声明的动态数组时的不同行为.
int ** arr ;
Run Code Online (Sandbox Code Playgroud)
编译器对它们的处理方式不同,因为a的每个索引都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
Run Code Online (Sandbox Code Playgroud)
假设是这种情况.这里2D Pointer的位置或数组1000.它保存2000自身保存内存位置地址的地址.这里指针算术由编译器完成,由此它判断元素的正确位置.
要分配内存2D Pointer,我们这样做..
arr = new int *[3] ;
Run Code Online (Sandbox Code Playgroud)
并为每个索引指针分配内存,这种方式..
for (auto i = 0 ; i < 3 ; ++i)
arr[i] = new int [4] ;
Run Code Online (Sandbox Code Playgroud)
在结束时,每个ptr的2D Array本身是一个数组.要访问你要做的元素......
arr[i][j] ;
Run Code Online (Sandbox Code Playgroud)
编译器这样做......
*( *(arr + i) + j ) ;
|---------|
1st step
|------------------|
2nd step
Run Code Online (Sandbox Code Playgroud)
在第一步中,将2D Array其取消引用到其适当的位置1D Array,在第二步中,1D Array取消引用以获得适当的索引.这就是为什么Dynamic 2D Arrays在没有提及行或列的情况下发送到函数的原因.
注意: 许多细节都被忽略了,描述中有很多东西,特别是内存映射只是为了给你一个想法.
你不能写void Foo(int bar[][]),因为bar衰变到一个指针.想象一下以下代码:
void Foo(int bar[][]) // pseudocode
{
bar++; // compiler can't know by how much increase the pointer
// as it doesn't know size of *bar
}
Run Code Online (Sandbox Code Playgroud)
因此,编译器必须知道大小*bar,因此必须提供最右侧数组的大小.
| 归档时间: |
|
| 查看次数: |
13955 次 |
| 最近记录: |