在C警告消息中将数组作为双指针传递

时间:2018-07-07 22:44:45

标签: c pointers

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

调用打印功能的语句

  

print(arr,3,4);

     

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

对于printf语句:

  

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

代码将2D数组传递给函数,该数组将其作为双指针接收并在函数内部打印出来。

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中将2D数组作为双指针传递是不正确的? 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;

3 个答案:

答案 0 :(得分:2)

您期望2D数组会衰减到指向指针的指针是没有根据的。

要使用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] {
    

这两项更改都将要求您也更改通话。

答案 1 :(得分: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运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,将类型为“ type 的数组”的类型转换为类型为“ pointer to type ”的表达式,该表达式指向数组对象的初始元素,而不是左值。 / p>

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

  • arr是一个标识符,表示它是某个对象的名称。这样,它指定其对象。该对象是3个4 int数组。
  • 此数组不是sizeofAlignof&的操作数,也不是字符串文字。因此,遵循规则,它从3个4 int数组的数组转换为指向第4个int第一个数组的指针。

接下来会发生什么?没有。我们拥有的表达式是一个指向4 int数组的指针。没有规则说将指向数组的指针转换为指向指针的指针。我们有一个指向数组的指针,但是该数组尚未在表达式中使用,尚未在简单表达式arr中使用。因此它不会转换。

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

(要查看它们是否不兼容,请考虑到指向4 int的数组的指针与指向int的指针的指针的区别。指向4 {的数组的指针的内存{1}}包含4个int值。指向int的指针的指针处的内存包含一个指针。这是完全不同的东西。)

接下来,考虑:

int

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

由于*((A+(m * 4) + n))是指向A的指针的指针,因此int意味着将A+(m * 4)添加到指针。 (顺便说一下,这是奇怪的间距。m * 4m的优先级乘以比4A的乘积更紧密,所以为什么(m * 4)会更好地刻画含义。)然后A + m*4意味着要在其中添加A+(m * 4) + n。总共,我们将n个元素移到了m*4+n指向的位置之外。由于A指向一个指针,因此我们将指针提高了A指针。然后m*4+n取消引用。当取消引用指向*((A+(m * 4) + n)))的指针的指针时,将获得指向int的指针。因此,该表达式的结果是一个指针。但是您想要一个int

您引用的链接涉及“ 2D数组”。它所涉及的数组类型是使用指向指针的指针实现的。若要创建这样的数组,请创建一个指针数组,然后将这些指针中的每一个设置为指向行的元素。然后,指向该指针数组的指针的作用类似于2D数组,因为int引用了行A[i][j]的元素j。如果具有这样的数组,则可以使用i来引用行n的元素m。同样,您可以使用A[m][n]来引用它。此表达式的意思是:

  • 获取指针*(*(A+m)+n)并向其添加A。由于m指向指向A的指针,因此添加int会使指针的值向前指向m指针。那是我们应该找到指向行m的元素的指针的地方。
  • m获取*(A+m)指向的指针的值。此值应该是指向行A+m的元素的指针,特别是指向第一个元素(索引为0)的指针。
  • m将指针的值向前移至指向点*(A+m)+n n。在这里我们应该找到行int的元素n
  • m获取*(*(A+m)+n)指向的int的值。

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

  • *(*(A+m)+n)是一个指向4 A数组的指针。向其添加int可使指针的值进一步指向m数组。
  • m获取*(A+m)指向的对象。该对象是整个数组。因此,这是一个指定数组的表达式。遵循有关表达式中数组的C规则,此数组将转换为指向其第一个元素的指针。因此,A+m成为指向数组*(A+m)的第一个元素的指针。
  • m将指针的值向前移至指向点*(A+m)+n n。在这里我们应该找到行int的元素n
  • m获取*(*(A+m)+n)指向的int的值。

因此,*(A+m)+n对于指针到指针,指针到数组和数组数组具有相同的最终结果,但是每个步骤所经过的步骤是不同的。 C知道每个子表达式的类型,并对它们进行不同的处理,以达到相同的结果。

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

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

答案 2 :(得分:0)

如果您的意图是一个指针数组,则无法一次将其全部初始化。您的数组编译为与{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]);
    }
}

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