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

问题描述 投票:3回答:2

我正在学习C函数如何接受多维数组,通过遵循C primer plus, by Stephen Prata (6th edition)一书中的处理。

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

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

或者,如果(且仅当)pt是函数的形式参数,则可以按如下方式声明:

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

据我所知,在C中,传入函数的数组会衰减为指向相应类型的指针。所以在ptint pt[][4]将衰变为int (* pt)[4],这是指向4个ints阵列的指针。

但我不明白为什么这种行为只发生'如果(并且只有)pt是函数的形式参数“。这是什么意思?为什么会这样?

c arrays pointers parameters implicit-conversion
2个回答
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

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 (*)[],它是一个指向不完整类型的数组指针,不允许作为函数参数。

© www.soinside.com 2019 - 2024. All rights reserved.