-2

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

4

3 回答 3

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 中)。

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

有关此处相关问题的更多信息。


更新:

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

常见陷阱 2: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)
于 2012-10-14T11:25:45.163 回答
2

如果您使用的是 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,“N-element array of T”类型的表达式将被转换(“decay”)为“pointer to T”类型的表达式,表达式的值将是第一个元素的地址大批。表达式array的类型为“ N-element array of M-element array of int”;当我们将它作为参数传递给 时blackandwhite,它被转换为类型为“指向-M元素数组的指针int”或的表达式int (*)[M],其值与&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); 也就是说,我们找到之后的i第 th 个元素a的地址并取消引用结果指针值。 a[i][j]被解释为*(*(a+i)+j)*(a + i)为我们提供了i'之后的数组a;通过上面提到的规则,这个表达式从数组类型转换为指针类型,值是第一个元素的地址。然后我们添加j到新的指针值并再次取消引用结果。

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

那为什么不做

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

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

编辑

在函数参数声明的上下文中,形式为T a[]or的参数声明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或者&是用于在声明中初始化另一个数组的字符串文字,例如

    char str[]="This is a test";
.

于 2012-10-14T12:59:51.410 回答
0

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

于 2012-10-14T11:13:29.587 回答