是否 和(* a)等效为函数参数?
function原型
void foo(int n, int a[][]);
给出了关于不完整类型的错误
void foo(int n, int (*a)[]);
编译。 根据衰减规则int a[][]
在这种情况下等效于int (*a)[]
,因此int (*a)[]
也应该给出关于不完整类型的错误,但GCC似乎接受它。 有什么我想念的吗? 这可能是一个GCC错误,但我没有发现任何与之相关的内容。
不,它们不等同于函数参数。 它们与foo
和bar
参数声明完全相同
struct S; void foo(struct S* s); // OK void bar(struct S a[]); // ERROR: incomplete type is not allowed
不等同。
C不允许将不完整类型作为数组元素(参见C 1999 6.7.5.2/1:“[…]元素类型不应是不完整或函数类型。[…]”)此限制适用于数组参数声明的方式与应用于任何其他数组声明的方式相同。 即使稍后将数组类型的参数隐式调整为指针类型,C也不会对函数参数列表中的数组声明提供特殊处理。 换句话说,在上述调整之前检查数组参数声明的有效性。
你的int a[][]
是一回事:尝试声明一个类型为int []
的数组,这是一个不完整的类型。 同时, int (*a)[]
是完全合法的 – 指向不完整类型的指针没什么不寻常的。
作为旁注,C ++“修复”了这个问题,允许在参数声明中使用不完整类型的数组。 但是,原始的C ++仍然禁止int a[][]
参数, int (&a)[]
参数甚至int (*a)[]
参数。 据推测,这可以在C ++ 17中修复/允许( http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#393 )
在不需要知道大小的上下文中允许不完整类型。
有了这个声明:
int a[][]
它甚至作为函数参数也是无效的,因为需要一个数组维的大小来知道如何在第二维上执行指针运算。
但这有效:
int (*a)[];
因为为了使用指向它的指针,不需要知道数组的大小。
C标准的 6.2.7节给出了这样一个声明的例子:
5示例给出以下两个文件范围声明:
int f(int (*)(), double (*)[3]); int f(int (*)(char *), double (*)[]);
由此产生的函数的复合类型是:
int f(int (*)(char *), double (*)[3]);
此示例显示类型为double (*)[3]
的声明,它与double (*)[]
类型的声明兼容
但是,由于缺少大小,您不能像2D数组那样直接使用它。 以下是一些示例。 如果您尝试这样做:
void foo(int n, int (*a)[]) { int i,j; for (i=0;i
编译器(如预期的那样)告诉你:
error: invalid use of array with unspecified bounds printf("a[%d][%d]=%d\n",i,j,a[i][j]); ^
你可以利用这样一个事实来解决这个问题:在大多数情况下,即使是不确定大小的数组也会衰减到指针:
#include void foo(int n, int (*a)[]) { int i,j; for (i=0;i
这使用以下输出编译清理:
a[0][0]=4 a[0][1]=5 a[1][0]=6 a[1][1]=7
即使在函数之外,也可以使用int (*)[]
语法:
#include int main() { int a[2][2] = { {4,5},{6,7} }; int i,j,n=2; int (*aa)[]; // "a" decays from int[2][2] to int (*)[2], assigned to int (*)[] aa = a; for (i=0;i
编辑
仔细阅读了标准的所有相关部分,C11 6.7.6.2和6.7.6.3, 我认为这是编译器错误/不符合。 它显然归结为委员会潜入关于数组分隔符的段落中间的文本。 6.7.6.2/1强调我的:
除了可选的类型限定符和关键字static之外,[和]可以分隔表达式或*。 如果它们分隔表达式(指定数组的大小),则表达式应具有整数类型。 如果表达式是常量表达式,则其值应大于零。 元素类型不应是不完整或函数类型。 可选的类型限定符和关键字static应仅出现在具有数组类型的函数参数的声明中,然后仅出现在最外层的数组类型派生中。
现在这当然写得非常糟糕,基本上它说
“外围特征兴趣不大,外围特征兴趣不大,外围特征兴趣不大, 这里的蓝色出现了与本段其余部分无关的一些arrays元素类型规格 ,外围特征兴趣不大,外围特征兴趣不大….”
所以很容易误解,欺骗了我。
这意味着int a[][]
总是不正确,无论它在何处声明,因为数组不能是不完整类型的数组。
但是,我在下面的原始答案提出了一些关于arrays衰减是否应该在编译器决定类型是否不完整之前或之后完成的有效问题。
给定具体情况void foo(int n, int a[][]);
只是,这是一个函数声明 。 这不是一个定义。
C11 6.7.6.3/12
如果函数声明符不是该函数定义的一部分,则参数可能具有不完整的类型
首先,允许参数在函数声明中具有不完整的类型。 标准很清楚。 这就是为什么这样的代码编译得很好:
struct s; // incomplete type void foo(int n, struct sa); // just fine, incomplete type is allowed in the declaration
此外:
C11 6.7.6.3/4
调整后,作为该函数定义一部分的函数声明符中参数类型列表中的参数不应具有不完整类型。
调整后在这里非常重要。
这意味着在将int a[][]
调整为int (*a)[]
,参数不应具有不完整的类型。 它没有,它是一个指向不完整类型的指针 ,它总是被允许并且非常精细。
不允许编译器首先int a[][]
作为不完整数组的不完整数组进行求值,然后再调整它(如果发现该类型不完整)。 这将直接违反6.7.6.3/4。