我目前正在学习C函数如何接受多维数组,方法是遵循本书C primer plus, by Stephen Prata (6th edition)中的处理。
在书中,作者提到处理多维数组的函数可以声明为......
void somefunction( int (* pt)[4] );
或者,如果(且仅当)pt是a的形式参数 函数,您可以按如下方式声明:
void somefunction( int pt[][4] );
据我所知,在C中,传入函数的数组会衰减为指向相应类型的指针。因此pt
中的int pt[][4]
会衰减为int (* pt)[4]
,这是指向4 int
个数组的指针。
但我不明白为什么只会发生这种行为'如果(且仅当)pt是函数的正式参数"。这是什么意思?为什么会这样?
答案 0 :(得分:4)
我认为他意味着你可能不会声明像这样的数组
int pt[][4];
在程序中(除了具有外部或内部链接的声明),因为它是一个不完整的类型,数组的大小是未知的。
但是,您可以将此类声明用作参数声明
void somefunction( int pt[][4] );
因为编译器将参数调整为类型
的指针void somefunction( int ( *pt )[4] );
和指针总是完整的类型。
结果例如这些函数声明
void somefunction( int pt[100][4] );
void somefunction( int pt[10][4] );
void somefunction( int pt[][4] );
是等效的,并声明相同的一个函数。
下面是一个使用外部链接声明不完整类型的数组(当声明不是定义时)的示例。
#include <stdio.h>
int pt[2][4];
int main(void)
{
extern int pt[][4];
printf( "sizeof( pt ) = %zu\n", sizeof( pt ) );
return 0;
}
程序输出
sizeof( pt ) = 32
答案 1 :(得分:2)
普通数组和函数参数数组之间的类似语法是一大堆混乱的根源。
如果声明一个普通数组,你可以这样做:
int arr[3] = {1,2,3};
或者你可以这样做:
int arr[] = {1,2,3};
这些表单是100%等效的,后者只是告诉编译器计算元素的数量并为你填写3
。
同样,您可以使用2D数组执行此操作,但只能使用最左侧的维度:
int arr[][3] = { {1,2,3}, {1,2,3} };
与int arr[2][3]
相同。
以上所有内容适用于任何普通的本地或全局数组。值得注意的是,上述仅适用,因为存在初始化列表。如果没有初始化列表,我们就无法编写int arr[];
- 这是无效的C语法。到目前为止一切都很好。
为了产生最大的混淆,C还允许将函数参数声明为void func (int arr[3]);
或void func (int arr[])
。事实证明,这两种形式也是等同的。但出于完全不同的原因!
因为在具体的函数情况下,数组声明总是被调整(“衰减”)成指向第一个元素的指针。所以int arr[3]
当函数声明的一部分时,100%等同于int*
。
当我们将int arr[]
写为函数参数时,它是一个不完整的类型,就像我们在上面没有初始化列表的情况下编写它一样。它不能被使用 - 它并不意味着“函数接受任何数组大小”,但在实践中将会发生这种情况。因为编译器不关心这里的数组大小,因为它是一个函数参数,无论如何编译器将用指向第一个元素的指针替换它。因此,无论我们在[ ]
之间输入什么,我们最终会得到int*
。
数组衰减规则适用于任何参数类型。因此,如果您使用2D数组(实际上是数组数组),它会衰减为第一个元素的指针。这与指向第一个数组的指针相同。所以你可以写int pt[][4]
,它会起作用。 int pt[666][4]
也是如此。无论如何,你最终会得到int (*)[4]
。
但是这个数组衰减规则并不适用于“递归”:C编译器只能看到一个数组。它恰好是一个数组数组,但编译器并不关心。所以我们不能写int arr[][]
,因为这会导致int (*)[]
这是一个指向不完整类型的数组指针,不允许作为函数参数。