为啥 C 函数不能返回数组类型? [复制]

Posted

技术标签:

【中文标题】为啥 C 函数不能返回数组类型? [复制]【英文标题】:Why can't C functions return an array type? [duplicate]为什么 C 函数不能返回数组类型? [复制] 【发布时间】:2015-12-13 18:28:39 【问题描述】:

我是 C 语言的新手,我想知道:

为什么 C 函数不能返回数组类型?

我知道数组名是数组第一个值的地址,而数组是 C 中的二等公民。

【问题讨论】:

相关帖子:***.com/questions/5157439/…。它是关于 C++ 的,但 C++ 这样做是因为 C。 如果你使用 C++11,你可以传递一个数组容器:std::array<int, 3> a2 = 1, 2, 3;,它具有像普通数组一样的运算符。 没有真正的数组是从 1970 年代的语言继承而来的。您可以通过将数组包装在 struct 中来解决此限制。 APL 可以追溯到 1960 年代,适用于“真实”数组,包括多维数组。回到问题,这是设计 C 语言以通过引用而不是通过值传递数组的人们做出的选择。 不同意将 C 问题链接到 C++ 副本 【参考方案1】:

因为在 C 中实际上没有任何类似于 Java 等语言的数组类型的数组类型。如果源代码提供了该信息,现代 C 编译器将执行一致性检查,但是有多年的 C 源代码是在现代编译器之前编写的,并且源代码需要与现代 C 编译器一起使用。

如果你愿意,你可以返回一个指向数组的指针,并且你可以使用与使用数组相同的指针语法,只要使用下标(例如,*aa[0] 相同,用于某些指针 @987654324 @)。

或者您可以返回 struct,然后您可以使用它来提供在 Java 等语言中看到的数组类型的一些功能。

因此您可以执行以下操作。

typedef  struct 
    things *array;
    int    iCount;
 MyArrayType;

然后你可以做一些事情,比如分配一个数组,然后返回它。

MyArrayType myFuncIntArray (int iCount)

    MyArrayType ret = 0;

    ret.array = malloc (iCount * sizeof(things));
    ret.iCount = iCount;

    return ret;

当然,您可以更改函数以创建您想要创建的任何类型的数组。不幸的是,您还需要负责在完成后释放内存,因为垃圾收集不是 C 的核心产品,尽管有 Boehm-Demers-Weiser garbage collector for C and C++。

C 之所以这样做,是因为设计它的聪明人是这样做的,而 C 标准委员会在大约 40 年里没有改变它。

C 的最初设计目标是代码的简单性和简洁性,以及通过函数库实现核心语言的可扩展性,以保持 C 编译器的简单和小巧,同时为程序员提供灵活性。生成的机器代码的高性能也是设计目标。然而,这些设计目标也将程序正确性和性能的重大责任放在了程序员身上。

【讨论】:

但是有数组类型。而你没有回答这个问题。 @juanchopanza,Java 具有数组类型,就像 C# 等大多数托管类型的语言一样。 C 有什么相当于数组的语义糖。因此,您可以像使用数组一样使用带索引的指针。 不会改变 C 具有数组类型的事实。它们的语义可能与其他语言中的数组不同,但这是另一回事。 @juanchopanza,“只是语义问题”?语义,意义,是所有交流的组成部分。不同的语义,不同的意义,不同的交流方式,不同的语言。类型数组和数组类型是有区别的。 @RichardChambers:鉴于int *ptr;int arr[1];,你认为ptrarr 具有相同的类型吗?仅仅因为数组很容易衰减为指针并不意味着它们是同一回事。【参考方案2】:

你自己已经回答了这个问题:数组是二等公民。

C 按值返回。数组不能传值,所以不能返回。

至于为什么数组不能按值传递:这是 K&R 在最初设计语言时做出的设计决定,现在更改它为时已晚,因为所有现有的 C 代码都会损坏。

【讨论】:

这个答案虽然正确,但并不能解释 K&R 设计决策的动机。有关 K&R 设计决策的简明理解,请参阅 John Bode 的回答,或参阅 Barmar 进行更长时间的讨论。【参考方案3】:

基于The Development of the C Language,看起来真正的原因主要与C从B和BCPL的演变有关。 B 没有数组变量,它只有指针。它也没有结构。当结构被添加到 C 中,并且它们被允许包含数组成员时,他需要一种方法来处理这些封闭的数组,类似于处理 B 样式数组的方式,解决方案是让数组在任何时候都转换为指针re 用在表达式中。这是论文中解释这一点的部分。

当我尝试扩展类型符号时,问题变得很明显,尤其是 添加结构化(记录)类型。结构似乎应该以直观的方式映射到内存 在机器中,但是在包含数组的结构中,没有好地方来存放指针 包含数组的基数,也没有任何方便的方式来安排它被初始化。为了 例如,早期 Unix 系统的目录条目在 C 中可能被描述为

struct 
     int inumber;
     char name[14];
  ;

我希望这个结构不仅可以描述一个抽象对象,还可以描述一个集合 可能从目录中读取的位。编译器可以在哪里隐藏指向的指针 语义要求的名称?即使结构被认为更抽象,并且 指针的空间可能会以某种方式隐藏,我该如何正确处理技术问题 在分配一个复杂的对象时初始化这些指针,也许是一个指定的对象 包含包含任意深度结构的数组的结构?

该解决方案构成了无类型 BCPL 之间进化链中的关键跳跃 并键入 C。它消除了指针在存储中的具体化,而是导致 在表达式中提到数组名称时创建指针。规则,生存下来 在今天的 C 语言中,数组类型的值在出现在表达式中时会被转换, 指向组成数组的第一个对象的指针。

这并不特定于将数组传入和传出函数,它适用于任何时候在表达式中使用数组(除非它是 & 运算符的参数)。

另请注意,此设计允许使用 malloc() 动态分配的数组变量和内存在传递给函数时被同等对待。

【讨论】:

Ritchie 写了一篇关于 C 语言发展的论文(包括它的数组语义)——PDF 是可用的here。 相关事实:在 K&R C 中,结构也不能按值传递或返回。这是在 ANSI C 中添加的。 @M.M 我对此有一个模糊的回忆,但我不确定。【参考方案4】:

不能从函数返回数组的原因是数组类型可能不是赋值的目标。你不能写类似的东西

int arr[N];
arr = foo();

当它们出现在大多数上下文中时,数组表达式失去了它们的“数组特性”。这是 Ritchie 在 C 开发早期做出的设计决定的结果。 C 源自一种称为 B 的早期编程语言(如图),而 B 又源自 BCPL。在 B 中,数组对象指向数组第一个元素的指针,并且数组访问是根据指针算法定义的。 a[i] 被定义为*(a + i); - 从a 中存储的地址偏移i 元素 并取消引用结果。

Ritchie 保留了 B 的数组语义,但不想搁置这些语义所需的指针存储。因此,他提出了在大多数情况下将数组表达式转换(“衰减”)为指针表达式的规则。

这意味着如果arr 是数组类型,arr = foo(); 将无法工作,因为没有为名为@9​​87654328@ 的对象预留存储空间,与数组元素本身分开;没有什么可以分配给

【讨论】:

以上是关于为啥 C 函数不能返回数组类型? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能在 Swift 中将对象数组返回给 UIStackView? [复制]

为啥从函数返回数组作为参数时,我会从函数中的数组中获取随机值? [复制]

为啥函数不能被返回类型重载? [复制]

Oracle 内存表(数组,函数返回值类型)

为啥我不能在函数中返回计算出的距离(来自 Google Maps API)? [复制]

为啥指针不能传递正确的值(C)?