【问题标题】:Trying to get difference of 2 2D arrays using pointers尝试使用指针获取 2 个二维数组的差异
【发布时间】:2023-01-15 19:21:03
【问题描述】:

我正在尝试使用二维数组的指针创建一个减法函数但是当我运行它时我得到

表达式必须具有指向对象的指针类型,但它具有类型“int”C/C++(142)

谁能解释为什么我会收到此错误以及解决此问题的更好方法是什么?

这是我的代码

读取数组的函数

int *readMatrix(int *arr)
{
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            printf("row %d, col %d: ", i + 1, j + 1);
            scanf("%d", &arr[i * 4 + j]);
        }
    }
    printf("\n");
    return arr;
}

减去 2 个二维数组的函数

int *subM(int *arrA, int*arrB, int *arrC){
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            //printf("row %d, col %d: ", i + 1, j + 1);
            &arrC[i][j] =  &arrA[i][j] - &arrB[i][j]; //code where I am getting error
        }
    }
    return arrC;
}

主功能

int main()
{
    int arrA[3][4];
    int arrB[3][4];
    int arrC[3][4];

    readMatrix(&arrA[3][4]);
    readMatrix(&arrB[3][4]);
    subM(&arrA[3][4],&arrB[3][4],&arrC[3][4]);
    return 0;
}

【问题讨论】:

  • int *arrC 不适合存储“二维数组”。看起来你是 C 的新手,并且猜测......这不是很好吗?我建议阅读教科书(例如 K&R2e),直到错误消息中的文字比代码更有意义。从 &amp;arr[i * 4 + j] 习语(不正确,但可能足够接近实际工作)到可疑的 &amp;arrC[i][j] 习语的转变是什么?你写了这段代码的全部吗?如果是这样,我很难理解你怎么没有发现这个......不一致......

标签: arrays c pointers


【解决方案1】:

我是 StackOverflow 的新手。如果我不能很好地表达自己,我很抱歉,但我想我找到了解决你问题的方法。

让我们一步一步地看这个。

  1. 将数组传递给函数时,不需要写下标。

    这意味着,而不是这个:

    readMatrix(&arrA[3][4]);
    

    只需这样写:

    readMatrix(arrA);
    

    您也可以(实际上,应该)删除指针运算符 (&),因为当仅使用数组名称时,它会自动充当指针。

    现在让我们看一下 readMatrix 的定义。

    int *readMatrix(int *arr)
    

    对多维数组使用指针是可以的,但是编译器会吐出很多警告。

    最标准的方法是在函数定义中使用下标:

    int *readMatrixStandard(int arr[3][4])
    {
        for (int i = 0; i < 3; ++i)
        {
            for (int j = 0; j < 4; ++j)
            {
                printf("row %d, col %d: ", i + 1, j + 1);
                scanf("%d", &arr[i][j]);
            }
        }
        printf("
    ");
        return arr;
    }
    
    1. subM中的下标

    对于您的情况,有两种方法可以访问多维数组。

    要么告诉编译器这个函数接受一个多维数组:

    而不是这个:

    int *subM(int *arrA, int*arrB, int *arrC)...
    

    做这个:

    int *subM(int arrA[3][4], int arrB[3][4], int arrC[3][4])...
    

    代码看起来像这样:

    int *subMMultiDimension(int arrA[3][4], int arrB[3][4], int arrC[3][4]){
        for (int i = 0; i < 3; ++i)
        {
            for (int j = 0; j < 4; ++j)
            {
                //printf("row %d, col %d: ", i + 1, j + 1);
                arrC[i][j] =  arrA[i][j] - arrB[i][j]; //code where I am getting error
                printf("%5d", arrC[i][j]);
            }
            puts(""); // for newline
        }
        return arrC;
    }
    

    或者使用一些 C/C++ 独有的指针魔术 :)(不要与上面的解决方案结合使用)

    而不是这个:

    int *subM(int *arrA, int*arrB, int *arrC){
        for (int i = 0; i < 3; ++i)
        {
            for (int j = 0; j < 4; ++j)
            {
                //printf("row %d, col %d: ", i + 1, j + 1);
                &arrC[i][j] =  &arrA[i][j] - &arrB[i][j]; //code where I am getting error
            }
        }
        return arrC;
    }
    

    尝试这个:

    int *subM(int *arrA, int *arrB, int *arrC){
        for (int i = 0; i < 3; ++i)
        {
            for (int j = 0; j < 4; ++j)
            {
                //printf("row %d, col %d: ", i + 1, j + 1);
                arrC[i * 4 + j] =  arrA[i * 4 + j] - arrB[i * 4 + j]; //code where I am getting error
            }
        }
        return arrC;
    }
    

    使用其中一种方法,但第一种似乎更标准,因为编译器不会对第一种发出警告。

    1. 返回值

    你可能知道这是怎么回事。我现在只是在敲代码。

    代替:

    return arr;
    return arrC;
    

    我更喜欢这个以减少警告:

    return arr[0];
    return arrC[0];
    

    原因很简单。它实际上指向相同的地址,但它让编译器闭嘴。


    我认为就是这样。最终代码如下所示:

    #include <stdio.h>
    
    int * readMatrixStandard(int arr[3][4])
    {
        for (int i = 0; i < 3; ++i)
        {
            for (int j = 0; j < 4; ++j)
            {
                printf("row %d, col %d: ", i + 1, j + 1);
                scanf("%d", &arr[i][j]);
            }
        }
        printf("
    ");
        return arr[0];
    }
    
    int * subMMultiDimension(int arrA[3][4], int arrB[3][4], int arrC[3][4])
    {
        for (int i = 0; i < 3; ++i)
        {
            for (int j = 0; j < 4; ++j)
            {
                //printf("row %d, col %d: ", i + 1, j + 1);
                arrC[i][j] =  arrA[i][j] - arrB[i][j]; //code where I am getting error
                printf("%5d", arrC[i][j]);
            }
            puts(""); // for newline
        }
        return arrC[0];
    }
    
    int main(void) // I recommend to always write void here if you are not using
                   // an old compiler
    {
        int arrA[3][4];
        int arrB[3][4];
        int arrC[3][4];
    
        readMatrixStandard(arrA);
        readMatrixStandard(arrB);
        subMMultiDimension(arrA,arrB,arrC);
        return 0;
    }
    

    编译很好,没有警告。


    这些代码 sn-ps 只是我的建议。如果你想知道用 C 做某事的最标准方法,你可能需要查找它。还推荐一本好书。例如,我通过 Stephen Prata 的 C Primer Plus 学习了 C。一本很棒的书,有很多例子和插图,可以帮助你了解情况。

    再次为我的英语感到抱歉。估计还有很长的路要走。

    如果我遗漏了什么或在某处犯了错误,请告诉我。

    编辑:是 Stephen Prata,不是 Stephan ;)

【讨论】:

    【解决方案2】:

    根据下标运算符[]的定义,表达式

    A[B]
    

    相当于:

    *(A + B)
    

    所以,

    A[B][C]
    

    相当于:

    *( *(A+B) + C )
    

    如果您将此应用到该行

    &arrC[i][j] =  &arrA[i][j] - &arrB[i][j];
    

    它相当于:

    &*( *(arrC+i) + j ) = &*( *(arrA+i) + j ) - &*( *(arrB+i) + j );
    

    表达方式

    &*( *(arrC+i) + j ) )
    

    无效,原因如下:

    子表达式

    *(arrC+i)
    

    具有类型 int,因为取消引用 int * 会产生 int。因此,子表达式

    *(arrC+i) + j
    

    也将评估为int

    在对该子表达式求值后,您尝试使用 * 运算符取消引用该 int,这是非法的。只能取消引用指针类型。

    子表达式

    *( *(arrA+i) + j )
    

    *( *(arrB+i) + j )
    

    有完全相同的问题。您还在这两个表达式中取消引用 int

    实际问题是您使用以下参数声明了函数subM

    int *subM(int *arrA, int *arrB, int *arrC)
    

    在 C 中,通常通过将(可能是decayed)指针传递给(外部)数组的第一个元素来将数组传递给函数。

    因此,如果将一维数组传递给函数,参数类型 int * 将是正确的,但对于二维数组则不正确。这是因为在您的情况下,指向二维 int 数组的外部数组的第一个元素的指针具有类型 int (*)[4],即指向 4 个 int 元素的一维数组的指针。但是,您传递的是指向单个 int 对象(而不是数组)的指针,因此您传递的指针类型错误。

    因此,您应该将参数类型更改为以下内容:

    int *subM( int (*arrA)[4], int (*arrB)[4], int (*arrC)[4] )
    

    写成下面的方式可能更清楚:

    int *subM( int arrA[3][4], int arrB[3][4], int arrC[3][4] )
    

    这两行是等价的,因为数组 decay 用作函数参数时指向指针。

    此外,您应该更改调用函数的方式。你应该换行

    subM(&arrA[3][4],&arrB[3][4],&arrC[3][4]);
    

    到:

    subM( arrA[3], arrB[3], arrC[3] );
    

    由于array to pointer decay,这一行相当于:

    subM( &arrA[3][0], &arrB[3][0], &arrC[3][0] );
    

    【讨论】:

      【解决方案3】:

      几个问题...

      1. readMatrix [正确] 使用了 int *arr arg。但是,我们希望它与sumM 兼容
      2. sumM 使用int * args,但尝试使用二维数组语法取消引用它们。
      3. sumM中,使用(例如)&amp;arr[i][j]地址的元素和不是它的价值[这就是我们想要的]。
      4. main,我们正在传递(例如)&amp;arr[3][4]。这一点过去结尾数组的,所以这是 UB(未定义的行为)。我们要传递数组的起始地址(例如arr&amp;arr[0][0])。
      5. 无需将指向结果数组的指针传回,因为调用者将地址作为参数传入。

        这是重构的代码。是这样注释的:

        #include <stdio.h>
        
        // Function to read array
        #if 0
        int *
        readMatrix(int *arr)
        #else
        void
        readMatrix(int arr[3][4])
        #endif
        {
            for (int i = 0; i < 3; ++i) {
                for (int j = 0; j < 4; ++j) {
                    printf("row %d, col %d: ", i + 1, j + 1);
        #if 0
                    scanf("%d", &arr[i * 4 + j]);
        #else
                    scanf("%d", &arr[i][j]);
        #endif
                }
            }
            printf("
        ");
        
        #if 0
            return arr;
        #endif
        }
        
        // Function to subtract 2 2d arrays
        #if 0
        int *
        subM(int *arrA, int *arrB, int *arrC)
        #else
        void
        subM(int arrA[3][4], int arrB[3][4], int arrC[3][4])
        #endif
        {
            for (int i = 0; i < 3; ++i) {
                for (int j = 0; j < 4; ++j) {
                    // printf("row %d, col %d: ", i + 1, j + 1);
        // NOTE/BUG: we want to use the _values_ and _not_ the _addresses_ of the
        // array elements
        #if 0
                    &arrC[i][j] = &arrA[i][j] - &arrB[i][j];
        #else
                    arrC[i][j] = arrA[i][j] - arrB[i][j];
        #endif
                }
            }
        
        // NOTE/BUG: since caller passed in the array, it knows where it is
        #if 0
            return arrC;
        #endif
        }
        
        // Main Function
        int
        main(void)
        {
            int arrA[3][4];
            int arrB[3][4];
            int arrC[3][4];
        
        // NOTE/BUG: doing (e.g.) &arrA[3][4] points past the _end_ of the 2D array
        // and, so, is UB (undefined behavior) -- we want to pass the start address
        #if 0
            readMatrix(&arrA[3][4]);
            readMatrix(&arrB[3][4]);
            subM(&arrA[3][4], &arrB[3][4], &arrC[3][4]);
        #else
            readMatrix(arrA);
            readMatrix(arrB);
            subM(arrA, arrB, arrC);
        #endif
        
            return 0;
        }
        

        在上面的代码中,我使用 cpp 条件来表示旧代码与新代码:

        #if 0
        // old code
        #else
        // new code
        #endif
        
        #if 1
        // new code
        #endif
        

        注意:这可以通过unifdef -k运行文件来清理

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多