【问题标题】:Passing multi-dimensional arrays into functions errors将多维数组传递给函数错误
【发布时间】:2021-03-12 10:20:14
【问题描述】:

我正在尝试将二维数组从主函数传递到函数中,但我不断收到错误消息。我究竟做错了什么? 代码如下:

void modifyArray(int *array);
int main(int argc, char *argv[])
{
int array[3][3];
modifyArray(&array, 3, 3, 3);
}

void modifyArray(int *array, int value, int row, int column)
{
array[row][column] = value;
}

错误:

 error #2140: Type error in argument 1 to 'modifyArray'; expected 'int *' but found 'int (*)[3][3]'.

提前致谢:)

【问题讨论】:

标签: arrays c pointers multidimensional-array parameter-passing


【解决方案1】:

请正确阅读错误信息,它解释了问题所在。

此外,您应该比您提到的错误更多,因为在modifyArray 函数中,您使用array 作为数组数组变量不是。更不用说你有两个不同的modifyArray 声明。

每本体面的初学者书籍、教程或课程都应该告诉您的是,数组可以衰减指向其第一个元素的指针。也就是说,在main 函数中,当您使用普通的array 时,它与&array[0] 相同。而由于array 的每个元素又是一个数组,那么&array[0] 将是一个指向这样一个数组的指针。

因此,传递数组的正确方法是将其传递为array,然后处理它所需的类型将是int (*)[3]

void modifyArray(int (*array)[3], int value, size_t row, size_t column)
{
    array[row][column] = value;
}

int main(void)
{
    int array[3][3];

    modifyArray(array, 3, 1, 1);  // Will be the same as array[1][1] = 3
}

另一方面,您当前的代码还为rowcolumn 传递了索引3。对于3 元素的数组,有效索引为02(含),这意味着3 的索引将超出范围。

【讨论】:

    【解决方案2】:

    int array[3][3];array 声明为由三个int 组成的三个数组组成的数组。

    &array 获取它的地址,因此结果是一个指向三个数组的指针,该数组由三个int 组成。

    modifyArray的声明中,int *array将参数array声明为指向int的指针。

    指向int 的指针与指向由三个int 组成的三个数组组成的数组的指针是不同的。所以编译器会抱怨。

    一般来说,如果某个东西被声明为foo,并且你将它传递给一个函数,你可以在函数声明中将它声明为foo

    void modifyArray(int array[3][3], int value, int row, int column);
    int main(int argc, char *argv[])
    {
        int array[3][3];
        modifyArray(array, 5, 1, 2);
    }
    

    C 确实使用数组表达式和参数进行了一些恶作剧。当参数声明为数组时,编译器会将其调整为指针。所以为参数声明int array[3][3]实际上声明了一个指向三个intint (*array)[3]的数组的指针。只调整数组的外层/基层参数类型;类型内的其他数组不会调整。

    类似地,当数组在表达式中用作sizeof 或一元& 的操作数之外,或者对于字符串字面量而言,用于初始化数组时,编译器会将其转换为指向第一个数组的指针元素。所以调用modifyArray(array, 5, 1, 2) 实际上将&array[0] 传递给函数。这与调整后的类型int (*array)[3] 匹配,因此可以正常工作。结果指针可以与通常的数组表示法一起使用,例如array[row][column]

    【讨论】:

      【解决方案3】:

      如果你有一个一维数组,那么这个函数声明

      void modifyArray(int *array);
      

      是正确的,因为它等价于下面的声明

      void modifyArray(int array[]);
      

      即具有数组类型的参数被编译器调整为指向数组元素类型的指针。

      虽然一般来说最好也传递数组中的元素数量,例如

      void modifyArray(int *array, size_t n);
      

      如果你想提供一个初始化器,那么函数声明可以是这样的

      void modifyArray(int *array, size_t n, int value );
      

      注意你程序中的两个函数声明是不同的

      void modifyArray(int *array);
      

      void modifyArray(int *array, int value, int row, int column)
      

      另外在函数内this声明

      array[row][column] = value;
      

      将调用未定义的行为,因为可以访问数组之外​​的内存。

      如果你有这样的数组

      int array[3][3];
      

      那么对应的函数参数会是这样的

      void modifyArray( int array[][3], size_t n, int value );
      

      或类似

      void modifyArray( int ( *array )[3], size_t n, int value );
      

      所以最好对整数文字 3 使用命名常量,比如

      #define  N  3
      

      或喜欢

      enum { N = 3 };
      

      函数声明看起来像

      void modifyArray( int ( *array )[N], size_t n, int value );
      

      main 中的数组声明如下所示

      int array[N][N];
      

      函数本身也可以这样调用

      modifyArray( array, N, 3 );
      

      如果你想将数组的所有元素设置为初始化器,那么你应该在函数中编写

      for ( size_t i = 0; i < n; i++ )
      {
          for ( size_t j = 0; j < N; j++ )
          {
              array[i][j] = value;
          }
      }
      

      如果你的编译器支持变长数组,那么函数声明可以是这样的

      void modifyArray( size_t rows, size_t cols, int array[][cols], int value );
      

      函数可以像这样调用

      modifyArray( 3, 3, array, 3 );
      

      在这种情况下,可以为具有任意行和列值的任何二维数组调用该函数。

      在函数中,循环看起来像

      for ( size_t i = 0; i < rows; i++ )
      {
          for ( size_t j = 0; j < cols; j++ )
          {
              array[i][j] = value;
          }
      }
      

      【讨论】:

      • 你的哥哥们已经给出了一些答案(:你给了他们什么额外的信息?
      • @snr 我确信我的回答提供了有用的附加信息。
      【解决方案4】:

      首先,无聊的背景:

      除非它是sizeof 或一元&amp; 运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则为“T 的N 元素数组”类型的表达式将被转换(“decay”)为类型为“pointer to T”的表达式,表达式的值将是第一个元素的地址。

      array 的类型是“int 的三元素数组的三元素数组”。除非它是sizeof 或一元&amp; 运算符的操作数,否则它会“衰减”为“指向int 的三元素数组的指针”或int (*)[3] 类型的表达式。表达式&amp;array 的类型为“指向int 的三元素数组的三元素数组的指针”,或int (*)[3][3]

      因此,您有几种可能的方法来处理这个问题。您可以保持函数调用不变,并对函数定义进行以下更改:

      modifyArray( &array, 3, 2, 2 ); // A 3-element array is only indexed from 0 to 2
      ...                             // so writing to array[3][3] writes outside the
                                      // bounds off the array, leading to undefined behavior
      
      void modifyArray(int (*array)[3][3], int value, int row, int column)
      {
        (*array)[row][column] = value;
      }
      

      或者,您可以更改函数调用以省略 &amp; 运算符:

      modifyArray( array, 3, 2, 2 );
      

      并将函数定义更改为

      void modifyArray(int (*array)[3], int value, int row, int column)
      {
        array[row][column] = value;
      }
      

      请记住,下标操作a[i] 等效于*(a + i) - 给定地址a,从该地址偏移i 元素(不是字节)并取消引用结果。 array 是一个 3 元素数组的开头地址,因此 array + i 产生该地址之后的第 i'th 3 元素数组的地址。因此,*(array + row)array[row] 相同。

      最终选项 - 传递指向第一个元素的指针并将其视为函数中的一维数组:

      modifyArray( &array[0][0], 3, 2, 2 );
      ...
      void modifyArray(int *array, int value, int row, int column)
      {
        array[row * 3 + column] = value;
      }
      

      前两个选项受到限制,因为它们只能处理特定大小的数组 - 第一个只能处理 3x3 数组,第二个只能处理 Nx3 数组。最后一个选项可以处理具有任意行数和列数的二维数组,您只需要找到某种方法来传递行数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-19
        • 2014-03-16
        相关资源
        最近更新 更多