【问题标题】:Passing a two-dimensional array to a function in C confusion将二维数组传递给C混淆中的函数
【发布时间】:2023-03-24 14:38:01
【问题描述】:

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

当在我下面的代码中将二维 [3] [4] 数组传递给函数时,编译器如何知道指针递增多远,在我们递增 3 的最后一个 printf() 的情况下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;

    }

【问题讨论】:

  • 我猜sizeof (int) 在您的实现中是 4。问题是,您正在增加 3 x sizeof (int) bytes (不是 3 * 4 * sizeof (int) 字节)。试试int arr[3][7] = ...
  • 我不明白
  • 假设您的值为 1 的元素位于地址 0x66600000。那么您的值为 2 的元素位于 0x66600000 + sizeof (int)(可能是 0x66600004)
  • 我理解的那部分,然后 arr[0] + 1,将是 0x6660000C,因为我们递增为 0x66600000 + ( 4 * sizeof (int) )...但 arr + 1 将递增为 0x66600000 + 3 * 4 * sizeof(int),但是编译器如何在函数内部知道二维数组的完整大小为 3 * 4 * sizeof(int)?
  • It doesn't。请注意此处的错误消息:wandbox.org/permlink/45ANpp3tESh3Dkqg,您有一个额外的间接级别。

标签: c pointers multidimensional-array


【解决方案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。结果可能是超出arr 四个字节的地址,具体取决于您的C 实现。 (它可能是不同数量的字节,但今天最常见的是四个。)

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

在此语句中,arr 被转换为指向其第一个元素的指针,即 4 个int 的数组。加 1 前进到指向下一个元素的指针,即 4 int 的下一个数组。所以这可能会给地址增加 16 个字节。

然后,在Fun

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

这里的arr 是一个指向4 个int 的数组的指针。它的值被打印出来,产生与main 中对应的printf 相同的地址。

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

这里arr[0]arr所指向的对象,它是一个由4个int组成的数组。由于它是一个数组,它会自动转换为指向其第一个元素的指针,即int。所以这指向第一个int。然后加 1 前进到下一个int,这再次产生与main 中对应的printf 相同的地址。

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,因此结果可能会超出 @987654375 的值 16 个字节@,这再次产生与main 中对应的printf 相同的地址。

如果您在Funmain 中看到printf 语句的不同值,这可能是因为int* 的声明不正确,并且因为int * 在您的C 实现中是8 个字节,相比之下四个int。该错误会使某些增量翻倍。您不应该在增量中看到任何三的倍数。

关于第一个维度,Fun 不需要知道第一个维度,因为它从不以第一个维度为单位推进任何指针。它只接收一个指向4个int数组的指针,它不需要知道那里有3个这样的数组。

【讨论】:

    【解决方案2】:

    详细的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;
    }
    

    【讨论】:

    • 谢谢。那很有意思。在这种情况下,编译器知道数组的完整大小。这是我之前的困惑,如果编译器不知道 [3][4],它怎么能知道 48 字节偏移量,但它不知道。为什么要在 printf() 的 tho 中强制转换为 (void *)
    猜你喜欢
    • 2021-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多