假设我有一个数组:array[2][4]
,在main方法中,我调用了一个函数blackandwhite
。如何将此方法作为参数传递给数组的长度和宽度?
答案 0 :(得分: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中),无法自动确定2D数组的行数/列数。
因此,需要将列数/行数作为单独的参数与指向2D数组的指针一起传递,如上例所示。
有关相关问题的更多信息 here 。
<强>更新强>
常见陷阱1:在参数列表中使用int** array
请注意,指向2维整数数组的指针仍然是指向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)
答案 1 :(得分: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 ,类型为“{-1}}的N元素数组”的表达式将被转换(“衰变”)为“指向{{1的指针”的表达式“,”,表达式的值将是数组中第一个元素的地址。表达式T
的类型为“T
- 元素数组array
- 元素数组N
”;当我们将它作为参数传递给M
时,它会转换为“指向int
的指针 - blackandwhite
的元素数组”或M
及其值的表达式与int
相同。
如果您使用的是不支持可变长度数组的C2011编译器(VLA支持现在是可选的),或者是C89或更早版本的编译器(从不支持VLA),那么您有两种选择:你可以硬编码列数:
int (*)[M]
在这种情况下,此函数仅适用于具有特定列数的数组,或者您可以使用theCodeArtist显示的方法,其中显式地将指针传递给数组的第一个元素以及行和列作为参数。但是,这意味着您将&array[0]
视为一维数组,而不是二维数组,这意味着您必须将二维索引映射到一维结构:
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);
请注意,此方法依赖于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
被解释为a
; a[i][j]
在*(*(a+i)+j)
之后为我们提供了*(a + i)
'数组;根据上面提到的规则,此表达式从数组类型转换为指针类型,值是第一个元素的地址。然后,我们将i
添加到新指针值,并再次取消引用结果。
我们可以使用a
将j
作为指向数组的指针(array[i][j]
或array
)或作为指针指针({{1})传递因为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);
; IOW,T a[]
将被声明为指针,而不是数组。同样,T a[N]
或T *a
形式的参数声明被视为a
;再次,T a[N][M]
被声明为指针,而不是数组。
在我们的第一个例子中,我们可以将T a[][M]
声明为
T (*a)[M]
或
a
但我更喜欢明确地使用指针声明,因为它正确地反映了正在发生的事情。
<小时/> 1。此规则的例外情况是数组表达式是blackandwhite
,void blackandwhite(size_t rows, size_t cols, int array[rows][cols])
或一元void blackandwhite(size_t rows, size_t cols, int array[][cols])
运算符的操作数,或者是用于在声明中初始化另一个数组的字符串文字,例如sizeof
。
答案 2 :(得分:0)
在C中,已知有一种计算字符串长度的方法,而在数组的情况下,您必须明确指定数组的尺寸,以便将长度和宽度传递给函数blackandwhite