在C混淆中将二维数组传递给函数

时间:2018-08-18 13:35:24

标签: c pointers multidimensional-array

我知道有很多类似的问题,但是我找不到答案。

在下面的代码中将二维[3] [4]数组传递给函数时,对于最后一个printf()我们要递增3的情况,编译器如何知道将指针递增多远x 4个内存位置,如果函数参数中缺少数字3? 我的意思是,为什么仅arr [] [4]足够,而不是[3] [4]?谢谢

#include <stdio.h>
#include <stdlib.h>

int Fun(int arr[][4])
{
    printf("%p\n", arr);         // address of first element
    printf("%p\n", arr[0] + 1);  // address increments by 4, pointing to next "inner array" 
    printf("%p\n", arr + 1);     // how does it know to increment address by 3 x 4 here? The complete array size
}

int main()
{
    int arr[3][4] =
    {
        1,2,3,4,
        5,6,7,8,
        9,10,11,12
    };

    printf("%p\n", arr);
    printf("%p\n", arr[0] + 1);
    printf("%p\n", arr + 1);

    printf("Passing to function\n");

    Fun(arr);

    return 0;

    }

2 个答案:

答案 0 :(得分:1)

首先,Fun的定义应为:

int Fun(int arr[][4])

int Fun(int* arr[][4]);而不是您拥有的东西。

接下来,当评估Fun(arr)时,arr会自动从3个4个int数组的数组转换为4个int数组的指针。同样,在Fun的声明中,int arr[][4]被自动调整为指向4个int数组的指针。因此,如果正确声明Fun,则参数类型和参数类型将匹配。

您也可以将Fun声明为:

int Fun(int (*arr)[4])

与上面相同,这是由于将对上述声明进行自动调整。请注意,此处的星号通过括号与arr分组。这使它成为指向int数组的指针,而不是指向int的指针数组。

现在,关于要打印的内容,在main中:

printf("%p\n", arr);

在此语句中,arr将自动转换为指向其第一个元素的指针,因此它将变为指向4 int的数组的指针。然后打印该指针的值。注意:从技术上讲,在打印指针时,应像const void *一样将它们转换为void *printf("%p\n", (const void *) arr);。但是,忽略这种情况目前不会造成问题。

printf("%p\n", arr[0] + 1);

在此语句中,arr[0]arr的第一个元素。该第一个元素是4 int的数组,它会自动转换为指向其第一个元素的指针。因此arr[0]成为第一个int的指针。然后加1将指针前进到下一个int。根据您的C实现,结果可能是地址arr后四个字节。 (它的字节数可以不同,但​​是今天最常见的是四个。)

printf("%p\n", arr + 1);

在此语句中,arr被转换为指向其第一个元素的指针,该元素为4个int的数组。加1会指向下一个元素的指针,该元素是下一个4 int的数组。因此,这很可能会将地址增加16个字节。

然后,在Fun中:

printf("%p\n", arr);         // address of first element

这里arr是指向4 int数组的指针。打印其值,其地址与printf中相应的main相同。

printf("%p\n", arr[0] + 1);  // address increments by 4, pointing to next "inner array" 

此处arr[0]arr指向的对象,它是4个int的数组。由于它是一个数组,因此它将自动转换为指向其第一个元素int的指针。因此,这指向第一个int。然后向下一个int加1前进,这又产生了与printf中相应的main相同的地址。

printf("%p\n", arr + 1);     // how does it know to increment address by 3 x 4 here? The complete array size

在这种情况下,arr是指向4 int的数组的指针,加1会将其前进到下一个4 int的数组,因此结果可能是16个字节超出了arr的值,这又产生了与printf中相应的main相同的地址。

如果您在printfFun中为main语句看到了不同的值,则可能是由于int*的声明不正确,以及int *在您的C实现中为8个字节,而int为4个字节。该错误会使某些增量加倍。您应该看不到增量的三个的倍数。

关于第一维,Fun不需要知道第一维,因为它永远不会以第一维为单位前进任何指针。它仅接收指向4个int数组的指针,并且不需要知道那里有3个此类数组。

答案 1 :(得分:1)

详细的answer by Eric Postpischil清楚地显示了OP代码中的所有问题。

我想指出,将指针传递给正确的类型会使编译器执行正确的指针算术:

#include <stdio.h>
#include <stdlib.h>

void Fun(int (*arr)[3][4])
{
    printf("Address of the first element:   %p\n", (void *)*arr);
    printf("Address of the second row:      %p\n", (void *)(*arr + 1));
    printf("Address after the last element: %p\n", (void *)(arr + 1));
}

void Fun_vla(size_t rows, size_t cols, int (*arr)[rows][cols])
{
    printf("Address of the first element:   %p\n", (void *)*arr);
    printf("Address of the second row:      %p\n", (void *)(*arr + 1));
    printf("Address after the last element: %p\n", (void *)(arr + 1));
}

int main()
{
    int arr[3][4] =
    {
        {1,2,3,4},
        {5,6,7,8},
        {9,10,11,12}
    };

    Fun(&arr);
    puts("");
    Fun_vla(3, 4, &arr);
    return 0;
}