如何将多维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具有相同的值。