【问题标题】:Why is type arrayp[] the same as type* arrayp as parameter of a function?为什么 type arrayp[] 与 type* arrayp 作为函数的参数相同?
【发布时间】:2020-04-24 07:29:12
【问题描述】:

为什么是 f.e. int 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 类型数组的第一个元素的指针?恕我直言,我认为这非常令人困惑。有些人可能认为他们用这个表达式传递了一个完整的数组(按值传递)。

【问题讨论】:

  • 您对“为什么?”的回应是什么?真的没什么好讨论的。您证明您已经理解它是 not 按值传递的,所以您真的想问“我如何按值传递数组”?
  • @PatrickRoberts 当然有“为什么”的答案。它们只是在 C 标准中找不到。
  • @PatrickRoberts:当然是有原因的,也有一些事情要讨论。当 C 刚被开发时,数组是一件大事,即使它只有几个元素,并且通过值(会占用太多堆栈和/或寄存器空间)或透明传递它会很笨拙参考(如果需要,安排编译器进行复制,并通过对程序员隐藏的参考传递)。当时,简单明了的解决方案是按地址传递数组,并且为了方便程序员,为此提供自动转换和调整。

标签: c arrays pointers parameter-passing pass-by-reference


【解决方案1】:

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

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

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中作为指针。该符号幸存下来的部分原因是 兼容性,部分原因是它允许的合理化 程序员向他们的读者传达传递 f a 的意图 从数组生成的指针,而不是对单个的引用 整数。不幸的是,它使学习者感到困惑,就像 提醒读者。

(注意: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 中描述为

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

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

...

【讨论】:

  • 不错的侦探!
【解决方案2】:

很难明确回答为什么的问题。在 C 语言中,数组/指针对偶性被到处使用。一旦你习惯了它,它就不会显得混乱了。

不要将其视为指向第一个元素的指针——将其视为指向整个数组的指针。 *a 是第一个元素,但*(a+1) 是下一个元素。

【讨论】:

  • 或者,更规范地说,a[0]a[1]
  • 如果我们把它看成是指向整个数组的指针,那么我们应该怎么看&amp;aa是一个数组,不是指针)?
  • @LxerLx 公平地说,无论签名是int arrayp[] 还是int* arrayp&amp;arrayp 都是一样的
  • @PatrickRoberts 我必须承认我的大脑在不同的环境中思考,其中a 是一个数组,其定义(因此大小信息)是可见的,而不是衰减为指针的函数参数.但同样,现在假设 arrayp 是一个声明为 int arrayp[]int *arrayp 的函数参数,如果我们将 arrayp 视为 指向整个数组的指针,那么应该我们想到了&amp;arrayp
  • @PatrickRoberts 实际上是这样。但这与arrayp指向整个数组的指针的“概念”不一致。
猜你喜欢
  • 2013-09-19
  • 2010-10-31
  • 2019-07-19
  • 1970-01-01
  • 2021-04-19
  • 2011-11-03
  • 1970-01-01
  • 2013-08-20
  • 1970-01-01
相关资源
最近更新 更多