首页 > 解决方案 > 为什么 type arrayp[] 与 type* arrayp 作为函数的参数相同?

问题描述

为什么 feint arrayp[]int* arrayp, 作为函数定义/声明中的参数相同?

 int* foo(int arrayp[]){}

指向int作为参数传递给调用者的数组的第一个元素的指针。

像:

 #include <stdio.h>

 int* foo (int arrayp[]);

 int main(void)
 {
    int a[] = { 10,5,4,2,1 };
    int *p;

    p = foo(a);

    printf("%d",*p);

    return 0;
 }

 int* foo(int arrayp[])
 {
     *arrayp = 0;
     return arrayp;          
 }

为什么int arrayp[]指向类型数组的第一个元素的指针int?恕我直言,我认为这非常令人困惑。有些人可能认为他们用这个表达式传递了一个完整的数组(按值传递)。

标签: carrayspointersparameter-passingpass-by-reference

解决方案


在函数参数类型中,数组被隐式调整为它们将衰减到的指针。

因此,例如,这些函数声明对是兼容的:

void foo(int a[]);
void foo(int *a);  //compatible declaration

void bar(int a[][2][3]);
void bar(int (*a)[2][3]);  //compatible declaration

Dennis Ritchie(语言作者)在https://www.bell-labs.com/usr/dmr/www/chist.html中解释了这个令人困惑的规则

此外,一些旨在缓解早期过渡的规则导致了后来的混乱。例如,函数声明中的空方括号

int f(a) int a[]; { ... } 是活化石,是 NB 声明指针方式的残余;a 仅在这种特殊情况下,在 C 中被解释为指针。该符号之所以得以保留,部分原因是为了兼容性,部分原因是因为它允许程序员向他们的读者传达传递从数组生成的 fa 指针的意图,而不是对单个整数的引用。不幸的是,它既使学习者感到困惑,也使读者警觉。

(注意:Chist.html 使用 K&R 风格的函数定义。上述函数定义的原型等效项是int f( int a[]){ ... }.)

简而言之,该规则的存在主要是为了简化从 B 到 C 的转换,并允许程序员向读者发出信号,指出需要指向数组第一个元素的指针,而不仅仅是指向单个元素的指针(并且这样做无需使用评论)。

函数类型的参数也以类似的方式调整为指针。

void takeFuncPtr(void Func(void));
void takeFuncPtr(void (*Func)(void)); //compatible declaration

一个可能更深层次的问题是“为什么数组在 C 中完全衰减为指针”?链接的文档也提供了该问题的答案:

...这些语义代表了从 B 的简单过渡,我用它们做了几个月的试验。当我尝试扩展类型表示法,尤其是添加结构化(记录)类型时,问题变得很明显。结构似乎应该以直观的方式映射到机器的内存中,但是在包含数组的结构中,没有好地方来存放包含数组基数的指针,也没有任何方便的方法来安排它初始化。例如,早期 Unix 系统的目录条目可能在 C 中描述为

结构 { int innumber; 字符名称[14];};

我希望该结构不仅可以表征抽象对象,还可以描述可能从目录中读取的位集合。编译器可以在哪里隐藏语义要求的名称指针?即使结构被认为更抽象,并且指针的空间可以以某种方式隐藏,我如何处理在分配复杂对象时正确初始化这些指针的技术问题,也许是一个指定结构包含包含任意深度结构的数组的结构?该解决方案构成了无类型 BCPL 和有类型 C 之间进化链中的关键跳跃。它消除了指针在存储中的物化,而是在表达式中提到数组名称时导致创建指针。规则,

...


推荐阅读