2017-06-15 151 views
0

比方说,我有两个数组,我将它们传递给函数:为什么我们在传递动态二维数组时不需要列数?

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; 
} 

为什么我们不能把经过都以类似的方式排列?

编辑:代码中有错误。

+3

'arr2'不是一个数组,而是一个指针,实际上是一个指向指针的指针。 – alk

+0

在任何一种情况下,函数都需要以某种方式知道指向数据的大小。不管语法如何,都没有办法。所以理想情况下,你会写'void func(size_t x,size_t y,int arr [x] [y])''。 – Lundin

+1

另请参阅[正确分配多维数组](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays),以消除对动态二维数组的误解。 – Lundin

回答

1

与你所说的相反,情况是这样的:你不必传递行数。当您通过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 

:假设数组的索引像这样工作。

如果您想使用指针手动执行该操作,它看起来像这样:&arr[0][0] + row * MAX_COL + col。在那个例子中,你也必须知道数组的列大小MAX_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。在数组数组(二维数组)的情况下,它衰减到一个指向其第一个元素的指针,这是一个指向单个数组的指针int (*arr)[MAX_COL]

简而言之:int arr[][MAX_COL]编译器具有所需的所有信息来寻址数组arr[row][col]

+0

我明白你的答案,它肯定有帮助。总之,如果我可以这样建议,将会更好:“以更简单的方式,int ** arr2是使用指针形成的整数的2-d网格,而int arr1 [] []是整数的一维数据结构,有关行结束的信息“。如果我理解错了,请纠正我。 –

+0

@AbhishekAgrawal:不知道你的意思是二维网格和一维数据结构。实际上所有的阵列,1D,2D,3D等都连续存储在内存中。 –

+0

是指向连续指针的指针,因为每一行都是在每次迭代中使用malloc分配的,所以第一行的最后一个元素的地址和第二行的第一个元素之间应该有一个跳转? –

0

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

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

当它是sizeof操作者的操作数时,操作者_Alignof或 一元&操作者,或是用于初始化数组文本字符串,具有 类型“”类型的阵列“”表达转换为表达式与类型“”指针为类型“”指向 到阵列对象的初始元素,不是左值。 [...]

因此,像

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

void func(int (*arr1) [5], int **arr2) //pointer to the starting element of an array of 5 ints 

的符号是等同的。

+0

请再详细解释一下:'衰减到指向第一个元素的指针'。 –

+0

是不是所谓的*最外层*(可忽略的)?我相信它被称为内存布局,而不是代币之间的代码位置。 –

+1

@AbhishekAgrawal实际上,函数array *参数*被“调整”为指向数组元素类型的指针。所以'int [42']被*调整为'int *'。 Array * decay *是当你用一个数组作为*参数*调用这样一个函数时发生的情况。而“二维数组”是一组数组。 – juanchopanza

0

实际上您只有一个整数数组(即int arr1[][5])和一个指向int的指针的指针,即int **arr2。即使像arr1[10][5]这样的数组在作为参数传递给函数时衰减到指向元素所在内存开始的指针,在内存布局和编译器如何处理访问这些指针。

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

关于存储器布局:

形式int [10][5]的2D整数数组表示为10个连续的“行”,每个包括5“列”(即积分值)。这个数组的大小是10 * 5 * sizeof(int),一个“行”的大小是5 * sizeof(int)

指向int int **p的指针只是一个指针;它的大小是sizeof(int**),即使你有“malloced”一系列积分指针lile p = malloc(10 * sizeof (int*));请注意0​​中的“*”,因为您创建了一个指向整数的指针序列,而不是整数序列。这是内存布局的主要区别:它不是一个整数的二维数组,而是一个指向整数的一维数组。如果实际上为“10”整数分配了10个“行”,则每行可以位于内存的不同部分。管理10×5积分值所需的空间是“10 * sizeof(int *)+ 10 * 5 * sizeof(int)”。

关于访问:

假设int arr[][5]类型,这是整数的2D阵列,一个变量,其中一列的大小是5和没有被确定的行数。非正式地,像int x = arr[3][4]那样的访问被转换成访问数组的第012个元素,即“行时间行加列”;注意 - 基于这个公式 - 编译器不需要知道数组实际具有多少行。

相比之下,我们假设一个int **p类型的变量。您可以将x = p[3][4]这样的访问视为等同于int *r = p[3]; int x = r[4];请注意,r类型为int *,即它是一个指针,然后r[4]取消引用此指针并返回一个整数值。

这是非常非正式的描述。 然而,主要问题是arr[][5]的内存布局仅包含连续的整数值,而int **arrr可能是指针的序列(或者甚至只是一个这样的指针),它们中的每一个可能指向一系列整数值(或者仅仅是一个整数值)。

相关问题