我今天早些时候在programmers.stackexchange上发布了这个question。我一直认为int (*)[]
不在函数参数中衰减到int **
但我对我的问题得到了多个回复,表明它确实存在。
我在我的函数参数中大量使用int (*)[]
,但现在我变得非常困惑。
当我使用gcc -std=c99 -pedantic -Wall
void function(int (*a)[])
{
sizeof(*a);
}
我收到此错误消息:
c99 -Wall -pedantic -c main.c -o main.o
main.c: In function ‘function’:
main.c:3:11: error: invalid application of ‘sizeof’ to incomplete type ‘int[]’
make: *** [main.o] Error 1
这表明*a
的类型为int []
,而非int *
。
有人可以解释int (*)[]
之类的内容是否会在函数参数中衰减到int **
并给我一些参考(可能来自标准文档),这证明了为什么会这样。
答案 0 :(得分:18)
传递给函数时,只有数组类型转换为指向其第一个元素的指针。 a
的类型为指向int
数组的指针,即它是指针类型,因此没有转换。
原型
void foo(int a[][10]);
编译器将其解释为
void foo(int (*a)[10]);
那是因为a[]
是数组类型。 int a[][10]
永远不会转换为int **a
。也就是说,answer中的第二段是错误的和误导性的。
作为函数参数,int *a[]
相当于int **
,因为a
属于数组类型。
答案 1 :(得分:7)
int (*)[]
是指向int
数组的指针。
在您的示例中,*a
可以衰减到int*
。但是sizeof(*a)
并没有腐烂;它本质上是sizeof(int[])
,无效。
a
根本无法衰退(它是一个指针)。
答案 2 :(得分:7)
N1256§6.7.5.3/ p7-8
7参数声明为' type ''的数组应为 调整为''合格指针 type '',其中类型限定符(如果有)是在数组类型派生的
[
和]
中指定的类型限定符。如果是关键字static
也出现在数组类型的[
和]
内 派生,然后对每个函数的调用,值的 相应的实际参数应提供对第一个的访问权限 数组的元素,其元素至少与指定的元素一样多 大小表达。8参数声明为''函数返回 type '' 被调整为''指向函数返回类型'的指针,如 6.3.2.1。
int (*)[]
是“指向int
数组的指针”。它是“ type ”的数组吗?不,这是一个指针。它是“函数返回类型”吗?不,这是一个指针。因此它没有得到调整。
答案 3 :(得分:2)
在int (*a)[]
的情况下,sizeof *a
由于一个原因不起作用:数组没有元素计数。如果没有元素计数,则无法计算大小。
因此,a
上的任何指针算法都不起作用,因为它是根据对象的大小定义的。由于数组的大小不确定,因此不能在指针本身上使用指针算法。数组表示法是根据指针算法定义的,因此sizeof a[0][0]
(或涉及a[n]
的任何表达式都不起作用,而sizeof (*a)[0]
将会工作。
这实际上意味着你可以用指针做很少的事情。唯一允许的是:
*
运算符如果您的编译器支持可变长度数组(VLA),并且您知道大小,则可以通过在函数体的开头添加一行来解决此问题,如
void
foo (int (*a0)[], size_t m, size_t n)
{
int (*a)[n] = a0;
...
}
如果没有VLA,您必须采取其他措施。
值得注意的是,动态分配不是int (*)[]
的一个因素。数组数组衰减到指向数组的指针(就像我们在这里一样),因此在将它们传递给函数时可以互换(sizeof
并且任何_Alignof
或typeof
关键字都是运算符,而不是功能)。这意味着必须静态分配指向的数组:一旦数组衰减到指针,就不会再发生衰减,所以你不能说一个指向数组的指针(int (*)[]
)与a相同指向指针(int **
)的指针。否则,您的编译器会很乐意让您将int [3][3]
传递给接受int **
的函数,而不是想要int (*)[]
,int (*)[n]
,int [][n]
形式的参数,或者int [m][n]
。
因此,即使您的编译器不支持VLA,您也可以使用静态分配的数组将其所有元素组合在一起的事实:
void foo (int (*a0)[], size_t m, size_t n)
{
int *a = *a0;
size_t i, j;
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
// Do something with `a[i * n + j]`, which is `a0[i][j]`.
}
}
...
}
用作二维数组的动态分配的一维数组具有相同的属性,因此这仍然有效。只有当第二个维度被动态分配时,意味着像for (i = 0; i < m; i++) a[i] = malloc (n * sizeof *a[i]);
这样的循环才能单独分配每个子数组,这个原则不起作用。这是因为你有一个指针数组(数组衰减后为int *[]
或int **
),它指向内存中另一个位置的数组的第一个元素,而不是数组数组,将所有物品放在一起。
所以:
不,int (*p)[]
和int **q
不能以相同的方式使用。 p
是指向数组的指针,这意味着所有项目都会从p
中存储的地址开始组合在一起。 q
是指向指针的指针,这意味着这些项可能分散在q[0]
,q[1]
,...,q[m - 1]
中存储的不同地址。
sizeof *p
无法正常工作,因为p
指向具有未知数量元素的数组。编译器无法计算每个元素的大小,因此p
本身的操作非常有限。