【问题标题】:Passing an array as double pointer in c warning message在 c 警告消息中将数组作为双指针传递
【发布时间】:2018-12-16 01:40:59
【问题描述】:

我正在尝试运行此代码,但出现编译器警告:

对于调用打印函数的语句

打印(arr,3,4);

从不兼容的指针类型传递 'print' 的参数 1 [-Wincompatible-pointer-types] main.c /arrays line 23 C/C++ 问题

对于 printf 语句:

格式“%d”需要“int”类型的参数,但参数 2 具有类型 'int *' [-Wformat=] main.c /arrays line 6 C/C++ 问题

代码是将二维数组传递给函数,该函数将其作为双指针接收并在函数内部打印。

void print (int **A, int m, int n){
    for(m = 0; m < 3; m++){
            for(n = 0; n < 4; n++){
                    printf("%d  ", *((A+(m * 4) + n)));
                }
                printf("\n");
        }
}

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

    print(arr,3,4);

    return 0;
}

1) 在 C 中将二维数组作为双指针传递不正确? here 上面的链接只指向 C++?或者这在 C 中也是可能的?

2) 如果我使用单个指针,那么以下哪些赋值是正确/不正确的?

清单 1:

int *ptr;
    ptr = &arr[0][0];
    for(int i = 0; i < 3; i++){
                for(int j = 0; j < 4; j++){
                        printf("%d  ", *(ptr + (i * 4) +j));
                    }
                    printf("\n");
            }

清单 2:

int * ptr; ptr = arr;

【问题讨论】:

  • 报错信息正确;但你的函数原型不是。你实际上是在传递一个指针数组吗?
  • 显然数组的内存布局其实和一维数组是一样的。此代码:pastebin.com/6hk63y9W 按顺序打印数字。我可以发誓这不是 C 编译二维数组的方式,但我想我错了。如果您尝试将其视为指向数组的指针数组,则会出现段错误。您可以做的是创建一个指针数组,然后将数组分配给这些指针。静态初始化它不会做你想要的。

标签: c pointers


【解决方案1】:

让我们开始吧:

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

print(arr,3,4);

print(arr,3,4); 中,arr 是一个数组。具体来说,它是一个3个元素的数组,每个元素是一个4个元素的数组,每个元素都是一个int。因此,arr 是一个由 3 个数组组成的数组,每组 4 个int。您可能听说过或读过数组“衰减”为指针。这是一个口语术语。您可以在 C 2011 标准的第 6.3.2.1 条第 3 段中找到实际规则:

除非它是sizeof 运算符、_Alignof 运算符或一元&amp; 运算符的操作数,或者是用于初始化数组的字符串文字,否则表达式类型为“array of type”被转换为类型为“pointer to type”的表达式,它指向数组对象的初始元素,而不是左值。

以下是此规则如何应用于print(arr,3,4); 中的arr

  • arr 是一个标识符,表示它是某个对象的名称。因此,它指定了它的对象。该对象是一个由 3 个数组组成的数组,每组 4 个 int
  • 这个数组不是sizeofAlignof&amp;的操作数,也不是字符串字面量。因此,按照规则,它从 3 个数组的 4 个int 的数组转换为指向第一个 4 个数组的指针 int

接下来会发生什么?没有。我们的表达式是一个指向 4 个int 的数组的指针。没有规定将指向数组的指针转换为指向指针的指针。我们有一个指向数组的指针,但该数组还没有在表达式中使用,也没有在简单的表达式arr 中使用。所以它没有被转换。

这意味着您传递给print 的是一个指向4 个int 的数组的指针。但是您对print 的声明说它需要一个指向int 的指针。这些是不同的东西,它们是不兼容的,所以编译器会警告你。

(要查看它们不兼容,请考虑指向 4 int 数组的指针和指向 int 指针的指针的区别。指向 4 int 数组的指针的内存包含 4 int 值。指向指向int 的指针的内存中包含一个指针。这些是非常不同的东西。)

接下来,考虑:

void print (int **A, int m, int n)
…
    printf("%d  ", *((A+(m * 4) + n)));

我们从上面知道你应该将int **A 更改为int (*A)[4],这是一个指向4 个int 的数组的指针。也可以改成int A[][4],因为C中有一条规则,这样的参数声明会自动调整为int (*A)[4],方便起见。但是,假设您将其保留为int **A。那么*((A+(m * 4) + n))是什么意思?

既然A是一个指向int的指针,那么A+(m * 4)的意思就是在指针上加上m * 4。 (顺便说一下,这是一个奇怪的间距。m4A(m * 4) 更紧密地被高优先级乘法约束,那么为什么它们的间距更松呢?@987654366 @ 会更好地描述含义。)然后A+(m * 4) + n 表示添加n。总的来说,我们已经将m*4+n 元素移到了A 指向的位置之外。由于A 指向一个指针,我们将指针推进了m*4+n 指针。然后*((A+(m * 4) + n))) 取消引用。当您取消引用指向int 的指针时,您将获得指向int 的指针。所以这个表达式的结果是一个指针。但你想要一个int

您引用的链接谈到“二维数组”。它所讨论的数组类型是使用指向指针的指针来实现的。要创建这样一个数组,您需要创建一个指针数组,然后将这些指针中的每一个设置为指向一行的元素。然后指向该指针数组的指针就像一个二维数组,其中A[i][j] 引用行i 的元素j。如果您有这样的数组,您可以使用A[m][n] 引用行m 的元素n。等效地,您可以使用*(*(A+m)+n) 引用它。这个表达式的意思是:

  • 获取指针A 并将m 添加到它。由于A 指向指向int 的指针,因此添加m 会使指针的值更进一步地指向m 指针。这就是我们应该找到指向行 m 的元素的指针。
  • *(A+m) 获取A+m 指向的指针的值。该值应该是指向行 m 的元素的指针,特别是指向第一个元素(索引为 0)的指针。
  • *(A+m)+n 将指针的值前进到指向 n int 更远。这就是我们应该找到行 m 的元素 n 的地方。
  • *(*(A+m)+n) 获取*(A+m)+n 指向的int 的值。

现在假设您将 print 更改为 print(int A[][4], int m, int n)。那么你的printf 语句应该像以前一样使用A[m][n]。或者它也可以像以前一样使用*(*(A+m)+n)。但是,在这种情况下,表达式会被计算:

  • A 是指向 4 个 int 的数组的指针。将m 添加到它会使指向m 数组的指针的值进一步前进。
  • *(A+m) 获取A+m 指向的对象。这个对象是一个完整的数组。所以这是一个指定数组的表达式。遵循关于表达式中数组的 C 规则,该数组被转换为指向其第一个元素的指针。因此,*(A+m) 成为指向编号为 m 的数组的第一个元素的指针。
  • *(A+m)+n 将指针的值前进到指向 n int 的位置。这就是我们应该找到行 m 的元素 n 的地方。
  • *(*(A+m)+n) 获取*(A+m)+n 指向的int 的值。

因此,A[m][n] 对于指针指向、指向数组的指针和数组中的数组具有相同的最终结果,但是每个步骤所经历的步骤不同。 C 知道每个子表达式的类型并对其进行不同的处理,以达到相同的结果。

最后,假设您将&amp;A[0][0] 传递给print 并将其参数更改为int *A。现在*((A+(m * 4) + n))) 是什么表达式?在这种情况下,您将 4 个 int 的 3 个数组的数组视为 12 个 int 的一个大数组。然后计算行m 的元素n 的位置。在这种情况下,A 是指向 int 的指针(不是指向 int 的指针的指针)。所以A+(m * 4) + n 是计算m 行的元素n 应该在哪里,*((A+(m * 4) + n))) 得到那个元素的值。

这是您应该尽可能避免的方法。通常,您应该使用 C 的内置方法来寻址数组元素,并避免自己进行计算。是否严格符合 C 代码可能取决于您对 C 标准中某些段落的解释程度。

【讨论】:

    【解决方案2】:

    您期望二维数组会衰减为指向指针的指针是没有根据的。

    为了能够使用arr 作为print 的参数,您有以下选择。

    1. print更改为

      void print (int (*A)[4], int m){ // Not need for n. It is 4
      
    2. 更改 print 以使用 VLA。为此,mn 必须在 A 之前。

      void print(int m, int n, int A[m][n] {
      

    这两项更改都需要您也更改调用。

    【讨论】:

    • 或者如果我写像 void print (int (*A)[3][4])?这还是错了吗?
    • @hago,这也是一个选项。如果你这样做,你必须在调用print时使用&amp;arr
    【解决方案3】:

    如果您的意图是一个指针数组,您不能一次将其全部初始化。你的数组编译成和{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}一样的东西

    以下编译为指向数组的指针数组:

    int arr1[] = {1, 2, 3, 4};
    int arr2[] = {5, 6, 7, 8};
    int arr3[] = {9, 10, 11, 12};
    int* my2DArr[3] = {arr1, arr2, arr3};
    

    对于这样的嵌套循环有效:

    for(int i = 0; i < 3; i++)
    {
        for(int j = 0; j < 4; j++)
        {
            printf("%d\n", my2DArr[i][j]);
        }
    }
    

    对于代码中的数组,您可以对其进行迭代,就像它只是一个数组一样。

    【讨论】:

      猜你喜欢
      • 2020-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-05
      • 1970-01-01
      • 2012-01-24
      • 1970-01-01
      相关资源
      最近更新 更多