【问题标题】:How to pass a multidimensional C array to a function? [duplicate]如何将多维 C 数组传递给函数? [复制]
【发布时间】:2017-07-15 16:12:48
【问题描述】:

我在大学课堂上学习了 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.

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


我的original question 被标记为重复项,由于我不知道如何删除重复项,所以我再次询问。

编辑:不是How to pass a multidimensional array to a function in C and C++ 的重复,因为我问的是如何更改函数调用本身,而不是函数签名。我知道其中一个函数是正确的,但我不确定是哪一个或您将如何调用它。

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

【问题讨论】:

  • 您可以通过编辑旧问题来删除重复的关闭,以明确它不是重复的(包括链接您引用的帖子),然后等待它得到足够的重新打开票。然后您做的是再次发布重复的问题。 help center 有更多关于如果您的问题被关闭该怎么办的信息;它在询问部分。
  • 这是部分您链接到的问题的副本。数组数组与指向指针的指针相同。参见例如this old answer of mine 解释原因。
  • 下次您的问题已关闭并且您希望重新打开它时,请不要接受答案。如果这样做,则表示您对它感到满意,因此没有理由重新打开它。

标签: c arrays pointers multidimensional-array


【解决方案1】:

但是,我的教授说定义中的星数取决于数组中的维数。所以,int[] 会变成int*int[][] 会变成int**,等等。

我真的,真的希望你只是误解了他的话,因为那是不正确的。

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

通过一些例子:

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

在对foo 的调用中,表达式arr 的类型为“int 的10 元素数组”。由于它不是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 *。爱荷华州,arr == &arr[0]

到目前为止一切顺利。现在让我们看一个二维数组:

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

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

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

还记得上面所说的“除非它是 ... 一元 & 运算符的操作数”吗?如果我们写&arr[0]arr[0] 的类型为“int 的 20 元素数组”,但它确实不会自动衰减为指针。因此,我们得到的不是int ** 类型的表达式,而是int (*)[20] 类型的表达式。再说一遍,arr == &arr[0]

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

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

这一次,表达式arr 的类型为“10 元素数组int 30 元素数组中的20 元素数组”。这一次,它“衰减”为一个类型为“pointer to 20-element array of 30-element array of 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-element 数组的指针与指向M-element 数组的指针是不同的类型,其中N != M。如果你的函数原型是

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

那么它将适用于Nx20 数组;您不能将指针传递给具有不同外部维度的数组:

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] );

【讨论】:

  • 不幸的是,我特别记得他说过维数是指针定义中的星数。感谢您的精彩解释,现在清楚多了,+1
【解决方案2】:

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

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

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

现在,如果 a 是多维数组,比如 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 的值是 a 的地址,即 *b 和 a 具有相同的值。 但是 **b 有指向 a 点的点。

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

【讨论】:

    【解决方案3】:

    仔细检查数据类型,你会明白的。

    对于函数void bar(int* ptr) {...,它需要一个int *,你必须通过一个。类似的东西

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

    会做的。

    也就是说,一般来说,数组与指针不同。在大多数情况下,数组会衰减为指向数组第一个元素的指针,但话又说回来,数据类型应该相同(或至少,兼容)。

    【讨论】:

    • Ohhhh... 这很有意义。那么期待int** 的人不正确?
    • @Dovahkiin 再次,仔细检查,你有int ** 可以通过吗?还是int (*)[4]?
    • 我猜不是……所以我的教授错了?
    • @Dovahkiin 详细地说,一个指向int 的指针和一个指向4 个ints 数组的指针......应该有一些区别,不是吗?
    • @Dovahkiin see this also
    猜你喜欢
    • 2011-06-13
    • 2014-01-15
    • 1970-01-01
    • 2016-01-30
    • 1970-01-01
    • 2011-02-19
    • 2014-11-19
    • 2014-03-16
    相关资源
    最近更新 更多