数组符号衰减为指针符号 - 仅适用于函数参数?

Array-notation decay to pointer-notation - only for function parameters?

我目前正在学习 C 函数如何接受多维数组,按照书中的处理方法 C primer plus, by Stephen Prata (6th edition)

作者在书中提到处理多维数组的函数可以声明为...

void somefunction( int (* pt)[4] );

Alternatively, if (and only if) pt is a formal parameter to a function, you can declare it as follows:

void somefunction( int pt[][4] );

据我所知,在 C 中,传递给函数的数组将衰减为指向相应类型的指针。所以int pt[][4]中的pt会衰减成int (* pt)[4],这是一个指向4int数组的指针。

但我不明白为什么只有当(且仅当)pt 是函数的形式参数时才会发生这种行为。这是什么意思?为什么会这样?

我认为他的意思是你不能像这样声明一个数组

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

普通数组和函数参数数组之间的相似语法是一大堆混乱的根源。

如果你声明一个普通数组,你可以这样做:

int arr[3] = {1,2,3};

或者你可以这样做:

int arr[] = {1,2,3};

这些形式是 100% 等价的,后者只是告诉编译器计算元素的数量并为您填写 3

同样,您可以使用二维数组执行此操作,但只能使用 left-most 维度:

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[])。事实证明,这两种形式也是等价的。但出于完全不同的原因!

因为在特定的函数情况下,数组声明总是被调整 ("decay") 为指向第一个元素的指针。所以 int arr[3] 当函数声明的一部分时,100% 等同于 int*.

当我们把int arr[]写成一个函数参数时,它是一个不完整的类型,就像我们上面写的没有初始化列表一样。它不能被使用 - 它并不意味着 "function accepting any array size" 尽管在实践中会发生这种情况。因为编译器不关心这里的数组大小,因为它是一个函数参数,无论如何编译器都会用指向第一个元素的指针替换它。所以无论我们在 [ ] 之间键入什么,我们最终都会得到 int*.

数组衰减规则适用于任何参数类型。所以如果你使用一个二维数组,它实际上是一个数组的数组,它会衰减为第一个元素的指针。这与指向第一个数组的指针相同。所以你可以写 int pt[][4] 并且它会起作用。 int pt[666][4] 也会如此。不管怎样,你最终都会得到 int (*)[4]

但是这个数组衰减规则不适用"recursively": C 编译器只看到一个数组。它恰好是一个数组的数组,但编译器并不关心。所以我们不能写 int arr[][],因为那样会导致 int (*)[],这是一个指向不完整类型的数组指针,不允许将其作为函数参数。