如何将多维C数组传递给函数?

我正在学习大学课程中的C和指针,我认为除了多维数组和指针之间的相似性之外,我对这个概念有很好的把握。

我认为,因为所有数组(甚至是多维数组)都存储在连续的内存中,你可以安全地将它转换为int* (假设给定的数组是一个int[] 。)但是,我的教授说明星中的星数。定义取决于数组中的维数。 因此, int[]将成为int*int[][]将成为int**等。

所以我写了一个小程序来测试这个:

 void foo(int** ptr) { } void bar(int* ptr) { } int main() { int arr[3][4]; foo(arr); bar(arr); } 

令我惊讶的是,编译器在两个函数调用上发出警告。

 main.c:22:9: warning: incompatible pointer types passing 'int [3][4]' to parameter of type 'int **' [-Wincompatible-pointer-types] foo(arr); ^~~ main.c:8:16: note: passing argument to parameter 'ptr' here void foo(int** ptr) ^ main.c:23:9: warning: incompatible pointer types passing 'int [3][4]' to parameter of type 'int *' [-Wincompatible-pointer-types] bar(arr); ^~~ main.c:13:15: note: passing argument to parameter 'ptr' here void bar(int* ptr) ^ 2 warnings generated. 

这里发生了什么? 你怎么能改变函数调用,所以其中一个会接受数组?


我的原始问题被标记为重复,因为我无法弄清楚如何删除重复我再次问它。

编辑:不是 如何将多维数组传递给C和C ++中的函数的重复,因为我问的是如何更改函数调用本身,而不是函数签名。 我知道其中一个function是正确的,我只是不确定你会选择哪一个或如何调用它。

编辑2:这是我的原始问题的副本,但原件被错误地标记为重复,无法得到答案,所以我重新问了它。

但是,我的教授说定义中的星数取决于数组中的维数。 因此, int[]将成为int*int[][]将成为int**等。

我真的, 真的希望你只是误解了他在说什么,因为这不正确。

除非它是sizeof或一元&运算符的操作数,或者是用于初始化声明中的字符数组的字符串文字,否则将转换(“衰减”的类型为“ N元素数组”的表达式)表达式“指向T指针”,表达式的值将是数组的第一个元素的地址。 如果T是一个数组类型,你最终会得到一个指向数组的指针,而不是一个指向指针的指针。

通过一些例子:

 int arr[10]; ... foo( arr, 10 ); // need to pass number of rows as a separate parameter 

在对foo的调用中,表达式arr具有类型“10-element array of int ”。 因为它不是sizeof或一元&运算符的操作数,所以它“衰减”到int *类型,表达式的值将是第一个元素的地址。 因此, foo的原型将是

 void foo( int *arr, size_t rows ); // or int arr[]; it means the same thing in this context 

请注意,这与表达式&arr[0]的结果类型相同。 arr[0]是一个int对象,所以&arr[0]给我们一个int * 。 IOW, arr == &arr[0]

到现在为止还挺好。 现在让我们看一下2D数组:

 int arr[10][20]; ... foo( arr, 10 ); 

在这种情况下,表达式arr具有类型“ 20元素数组的 10元素数组”; 它“衰减”到“指向20个元素的int数组的指针”类型的表达式; 因此, foo的原型变成了

 void foo( int (*arr)[20], size_t rows ); // or int arr[][20] 

请记住上面所说的“除非它是……一元&算子的操作数”吗? 如果我们写&arr[0]arr[0]类型为“20-element array of int ”,但它不会自动衰减为指针。 因此,我们得到一个类型为int (*)[20]的表达式,而不是获得int **类型的表达式。 再次, arr == &arr[0]

现在让我们看一下3D数组:

 int arr[10][20][30]; ... foo( arr, 10 ); 

这次,表达式arr具有类型“10元素数组的20元素数组的30元素数组 ”。 这一次,它“衰变”为类型“指向20个元素的int元素数组的指针”的表达式,原型现在是

 void foo( int (*arr)[20][30], size_t rows ); // or int arr[][20][30] 

再次, arr[0]有一个数组类型,所以表达式&arr[0]给我们类型int (*)[20][30] ; 再次, arr == &arr[0]

从现在开始,高维数组的模式应该是清晰的。

现在,这带来了一个小问题。 指向N元素数组的指针与指向M元素数组的指针的类型不同,其中N != M 如果您的函数原型是

 void foo( int (*)[20], size_t rows ); 

然后它只适用于N x20arrays; 你不能将指针传递给具有不同外部维度的数组:

 void foo( int (*ap)[20], size_t rows ); ... int arr1[10][20]; int arr2[20][20]; int arr3[20][30]; foo( arr1, 10 ); // okay foo( arr2, 20 ); // okay foo( arr3, 20 ); // not okay - arr3 has type int (*)[30], which is not *compatible* // with int (*)[20] 

编辑

在函数期望int *并且您有多维数组的情况下,您将显式传递指向第一个元素的指针:

 void foo( int *, size_t size ); ... int arr2[10][20]; int arr3[20][10][5]; foo( &arr2[0][0], sizeof arr2 / sizeof arr2[0][0] ); foo( &arr3[0][0][0], sizeof arr3 / sizeof arr3[0][0][0] ); 

仔细检查数据类型,你会得到它。

对于函数void bar(int* ptr) {...它需要一个int * ,你必须传递一个。 就像是

  bar (&(arr[0][0])); 

会这样做的。

也就是说,一般来说,数组与指针不同。 对于大多数情况,数组衰减到指向数组的第一个元素的指针,但是再次,数据类型应该相同(或至少是兼容的 )。

假设,a = [1,2,3]

这里,a是指向数组第一个元素的指针。

 *a = 1 , *(a+1) = 2, *(a+2) = 3. 

现在,如果a是多维数组,就像[3] [4]

 *a = a[0][0] , *(a+1) = a[1][0], *(a+2) = a[2][0] *(*a + 1) = a[0][1], *(*a + 2) = a[0][2] *(*(a+1) + 1) = a[1][1], *(*(a+1) + 2) = a[1][2] *(*(a+2) + 1) = a[1][1], *(*(a+2) + 2) = a[1][2] 

* a + any_number更改行,但*(* a + any_number)更改列。

在函数中,您只将指针传递给数组,此处即a。

所以,

 void bar(*a){ #access elements like told above. } 

双指针** b,表示它指向另一个指针,表示如果

 *b = a 

b的值是ie的地址,* b和a具有相同的值。 但** b有点指向哪里。

表示** b和* a具有相同的值。