C++ 为什么我们不';传递动态2d数组时不需要列数?

C++ 为什么我们不';传递动态2d数组时不需要列数?,c++,c,arrays,C++,C,Arrays,假设我有两个数组,并将它们传递给一个函数: void func(int arr1[][4], int **arr2) { // <- I need to give n in only one, why? ... } int main() { int n = 5, m = 4; int arr1[n][m]; int **arr2 = (int**)malloc(n * sizeof(int*)); for(int i = 0;i < n;i++)

假设我有两个数组,并将它们传递给一个函数:

void func(int arr1[][4], int **arr2) { // <- I need to give n in only one, why?
...
}
int main() {
    int n = 5, m = 4;
    int arr1[n][m];
    int **arr2 = (int**)malloc(n * sizeof(int*));
    for(int i = 0;i < n;i++)
        arr2[i] = (int*)malloc(m * sizeof(int));
    func(arr1, arr2);
    return 0;
}

void func(int arr1[][4],int**arr2){/实际上恰恰相反,您只能省略其中一个索引(在多维数组的情况下),即最里面的索引

这是因为,数组作为函数参数传递时,会衰减为指向第一个元素的指针。引用
C11
,第§6.3.2.1章

除非它是
sizeof
运算符、
\u
运算符或 一元
&
运算符,或是用于初始化数组的字符串文字,一个具有 类型“”的数组已转换为类型为“”指针指向类型“”的表达式 数组对象的初始元素
,并且不是左值。[…]

因此,像

 void func(int arr1[][5], int **arr2)    //an array of array of 5 ints


是等效的。

实际上恰恰相反,您只能省略其中一个索引(在多维数组的情况下),即最里面的索引

这是因为,数组作为函数参数传递时,会衰减为指向第一个元素的指针。引用
C11
,第§6.3.2.1章

除非它是
sizeof
运算符、
\u
运算符或 一元
&
运算符,或是用于初始化数组的字符串文字,一个具有 类型“”的数组已转换为类型为“”指针指向类型“”的表达式 数组对象的初始元素
,并且不是左值。[…]

因此,像

 void func(int arr1[][5], int **arr2)    //an array of array of 5 ints


是等效的。

实际情况与您所说的相反:您不必传递行数。假设数组索引的工作方式如下:

int arr[MAX_ROW][MAX_COL]; /* with both 3 */

           col
     --------------->
    | 0,0   0,1   0,2
row | 1,0   1,1   1,2
    V 2,0   2,1   2,2
当您传递
int arr[][MAX\u COL]
时,编译器知道下一行将从哪里开始,例如,当您处理
arr[row][COL]
这样的地址时

如果您使用指针手动执行此操作,它看起来像:
&arr[0][0]+行*MAX\u COL+COL
。在该示例中,您还必须知道数组的列大小
MAX\u COL
,以计算下一行

原因是,数组在内存中是连续的。上面的数组在内存中表示为:

|     row = 0     |     row = 1     |     row = 2     |
| 0,0   0,1   0,2 | 1,0   1,1   1,2 | 2,0   2,1   2,2 |
编译器还必须知道行偏移量,因为当您将声明为
int arr[MAX_SIZE]
的数组传递给函数
void foo(int arr[])
时,它会衰减为指向数组开头的指针
int*arr
。对于数组(2D数组)的情况,它也会衰减为指向其第一个元素的指针,即指向单个数组的指针
int(*arr)[MAX\u COL]


简而言之:使用
int arr[][MAX_COL]
编译器拥有使用
arr[row][COL]
对数组进行寻址所需的所有信息。实际上,与您所说的情况相反:您不必传递行数。假设数组索引是这样工作的:

int arr[MAX_ROW][MAX_COL]; /* with both 3 */

           col
     --------------->
    | 0,0   0,1   0,2
row | 1,0   1,1   1,2
    V 2,0   2,1   2,2
当您传递
int arr[][MAX\u COL]
时,编译器知道下一行将从哪里开始,例如,当您处理
arr[row][COL]
这样的地址时

如果您使用指针手动执行此操作,它看起来像:
&arr[0][0]+行*MAX\u COL+COL
。在该示例中,您还必须知道数组的列大小
MAX\u COL
,以计算下一行

原因是,数组在内存中是连续的。上面的数组在内存中表示为:

|     row = 0     |     row = 1     |     row = 2     |
| 0,0   0,1   0,2 | 1,0   1,1   1,2 | 2,0   2,1   2,2 |
编译器还必须知道行偏移量,因为当您将声明为
int arr[MAX_SIZE]
的数组传递给函数
void foo(int arr[])
时,它会衰减为指向数组开头的指针
int*arr
。对于数组(2D数组)的情况,它也会衰减为指向其第一个元素的指针,即指向单个数组的指针
int(*arr)[MAX\u COL]


简而言之:使用
int arr[][MAX_COL]
编译器拥有使用
arr[row][COL]
对数组进行寻址所需的所有信息。实际上,您只有一个int数组(即
int arr1[][5]
)和一个指向int指针的指针,即
int**arr2
。即使像
arr1[10]这样的数组[5] 
,当作为参数传递给函数时,会衰减为指向元素所在内存开头的指针,内存布局和编译器处理这些指针访问的方式存在(很大)差异

顺便说一句,它主要应该是
intn=5,m=4;intarr1[m][n]
,而不是
intn=5,m=4;intarr1[n][m]

关于内存布局:

格式为
int[10][5]
的二维整数数组表示为10个连续的“行”,每个行由5个“列”(即整数值)组成。该数组的大小为
10*5*sizeof(int)
,一个“行”的大小为
5*sizeof(int)

指向int
int**p
的指针只是一个指针;它的大小是
sizeof(int**)
,即使你有一系列完整的指针lile
p=malloc(10*sizeof(int*))
;注意
sizeof(int*)中的“*”
,当你创建一个指向整数的指针序列,而不是一个整数序列时。这是内存布局的主要区别:它不是一个二维整数数组,而是一个指向整数的一维指针数组。如果你真的为“10”分配了10行整数,每行可以位于内存的不同部分。管理10x5整型值的(扩展)量所需的空间为“10*sizeof(int*)+10*5*sizeof(int)”

关于访问:

让我们假设一个
int arr[][5]
类型的变量,它是一个二维整数数组,其中