【问题标题】:How to pass a method the height and width of a 2d array as parameters?如何将二维数组的高度和宽度作为参数传递给方法?
【发布时间】:2012-10-04 14:28:30
【问题描述】:

假设我有一个数组:array[2][4],在 main 方法中,我调用了一个函数 blackandwhite。如何将数组的长度和宽度作为参数传递给这个方法?

【问题讨论】:

  • 可能是blackandwhite(array, 2, 4);?

标签: c arrays function multidimensional-array


【解决方案1】:

众所周知,在 C 中存在一种计算字符串长度的方法,而在数组的情况下,您必须显式指定数组的维度,以便将长度和宽度传递给函数blackandwhite

【讨论】:

    【解决方案2】:

    这是一个可能的解决方案:

    void blackandwhite(int* array, int height, int width)
    {
        // Array-processing done here.
        // array is pointer to int,
        // initially points to element myarray[0][0].
        // variable height = 2;
        // variable width = 4;
    
    }
    
    
    int main()
    {
        int myarray[2][4];
        blackandwhite(&myarray[0][0], 2, 4);
    }
    

    可以通过以下构造找到数组的大小,即其中元素的数量:

    int array[8];
    int size = sizeof(array)/sizeof(array[0]);
    

    不幸的是,C 数组是原生数组,不包含任何嵌入其中的元数据。行和列只是表示/访问内存中本质上是线性存储空间的一种方式。 AFAIK,没有办法自动确定二维数组的行数/列数,给定一个指向它的指针(在 C 中)。

    因此需要将列/行数作为单独的参数以及指向二维数组的指针传递,如上例所示。

    有关相关问题的更多信息here


    更新:

    常见陷阱 1:在参数列表中使用 int** array
    请注意,指向二维整数数组的指针仍然是指向 int 的指针。
    int** 意味着参数是指向指向 int 的指针的指针,而不是这里的情况。

    常见陷阱二:在参数列表中使用int[][]
    未能传递数组的维度。不需要传递数组第一维的大小(但你可以但编译器会忽略它)。尾随尺寸是强制性的。所以,

    // is INVALID!
    void blackandwhite(int array[][], int height, int width)
    
    // is VALID, 2 is ignored.
    void blackandwhite(int array[2][4], int height, int width)
    
    // is VALID.
    void blackandwhite(int array[][4], int height, int width)
    

    【讨论】:

    • 我明白,但你能解释一下为什么你在blackandwhite 方法中使用int* array 吗?
    • 哦,好的。指针和数组密切相关,因为每当您在 C 中声明一个数组时,您都会得到一个同名的指针(绝对免费)。该指针指向数组的“第零个”元素。指针的类型与数组中各个元素的类型相同。所以在上面的例子中,myarray(main 中声明)是一个整数数组,因此 array(黑白中声明的参数)是一个指向整数的指针。
    • C 中传递数组的更详细解释:stackoverflow.com/questions/1106957/…
    • 你应该把blackandwhite(myarray, 2, 4)改成blackandwhite(&myarray[0][0], 2, 4),否则编译器会报错
    • 另外,在 C 中,二维数组的第一个索引通常是高度,第二个是宽度;你可以做任何你想做的事,但这个版本不会那么混乱:void blackandwhite(int* array, int height, int width)
    【解决方案3】:

    如果您使用的是 C99 编译器,或支持可变长度数组的 C2011 编译器,您可以执行以下操作:

    /**
     * the cols parameter must be declared before it is used in
     * the array parameter.
     */
    void blackandwhite(size_t rows, size_t cols, int (*array)[cols])
    {
      size_t i, j;
    
      for (i = 0; i < rows; i++)
        for (j = 0; j < cols; j++)
          array[i][j] = ...;
    }
    

    你会这样称呼它

    int array[N][M];
    ...
    blackandwhite(N, M, array);
    

    在大多数情况下1,“T的N元素数组”类型的表达式将被转换(“衰减”)为“指向T的指针”类型的表达式,并且表达式的值将是数组中第一个元素的地址。表达式array 的类型为“N-M 的元素数组-int 的元素数组”;当我们将它作为参数传递给blackandwhite时,它被转换为“指向M-int的元素数组的指针”或int (*)[M]类型的表达式,其值与&amp;array[0]相同。

    如果您使用的 C2011 编译器支持可变长度数组(VLA 支持现在是可选的),或者 C89 或更早版本的编译器(从不支持 VLA),那么您有两种选择:您可以硬编码列数:

    void blackandwhite(size_t rows, int (*array)[M])
    {
       size_t i,j;
       for (i = 0; i < rows; i++)
         for (j = 0; j < M; j++)
           array[i][j] = ...;
    }
    
    ...
    blackandwhite(N, array);
    

    在这种情况下,此函数仅适用于具有特定列数的数组,或者您可以使用 CodeArtist 显示的方法,在该方法中,您将指针与行和列一起显式传递给数组的第一个元素作为参数。但是,这意味着您将把 array 视为一维数组,而不是二维数组,这意味着您必须将二维索引映射到一维结构:

    void blackandwhite(int *array, size_t rows, size_t cols)
    {
      size_t i, j;
    
      for (i = 0; i < rows; i++)
        for (j = 0; j < cols; j++)
          array[i * rows + j] = ...; // use a 1D index
    }
    ...
    blackandwhite(&array[0][0], N, M);
    

    请注意,这种方法依赖于array 的所有行和列在内存中是连续的;如果你已经动态分配了array 这样

    int **array = malloc(N * sizeof *array);
    if (array)
    {
      size_t i;
      for (i = 0; i < N; i++)
        array[i] = malloc(M * sizeof *array[i]);
    }
    

    那么就不能保证行在内存中是连续布局的。但是,在这种特定情况中,您可以编写以下内容:

    void blackandwhite(int **array, size_t rows, size_t cols)
    {
      size_t i, j;
    
      for (i = 0; i < rows; i++)
        for (j = 0; j < cols; j++)
          array[i][j] = ...;
    }
    
    ...
    blackandwhite(array, N, M);
    

    困惑了吗?

    记住表达式a[i]被视为*(a + i);也就是说,我们在a 之后找到ith 元素 的地址,并取消引用结果指针值。 a[i][j] 被解释为*(*(a+i)+j)*(a + i) 给了我们i'th array 跟随a;通过上面提到的规则,这个表达式从数组类型转换为指针类型,值是第一个元素的地址。然后我们将j 添加到新的指针值并再次取消引用结果。

    我们可以使用array[i][j],其中array作为指向数组的指针(int (*array)[cols]int (*array)[M])或作为指向指针的指针(int **array)传递,因为array[i]的结果是指针类型或衰减为指针类型的数组类型,可以将下标运算符应用于它。我们不能使用array[i][j],其中array 作为指向int 的简单指针传递,因为在这种情况下array[i] 的结果不是指针类型。

    为什么不这样做

    void blackandwhite(int **array, size_t rows, size_t cols)  {...}
    ...
    int array[N][M];
    blackandwhite((int **) array, N, M);
    

    指针和数组是不同的东西,所以指向指针的指针和指向数组的指针是不同的东西。这可能会起作用,但你在对编译器撒谎。最重要的是,这是一种糟糕的形式。

    编辑

    在函数参数声明的上下文中,T a[]T a[N] 形式的参数声明将被解释为T *a; IOW,a 将被声明为指针,而不是数组。类似地,T a[N][M]T a[][M] 形式的参数声明被视为T (*a)[M];同样,a 被声明为指针,而不是数组。

    在我们的第一个示例中,我们可以将 blackandwhite 声明为

    void blackandwhite(size_t rows, size_t cols, int array[rows][cols])
    

    void blackandwhite(size_t rows, size_t cols, int array[][cols])
    

    但我更喜欢明确地使用指针声明,因为它正确地反映了正在发生的事情。


    1。此规则的例外情况是数组表达式是 sizeof_Alignof 或一元 &amp; 运算符的操作数,或者是用于在声明中初始化另一个数组的字符串文字,例如
     
        char str[]="This is a test";
    .

    【讨论】:

      猜你喜欢
      • 2015-11-29
      • 2012-08-06
      • 1970-01-01
      • 2020-09-21
      • 2012-01-22
      • 1970-01-01
      • 2011-06-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多