为啥 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 编译器一起使用。
如果你愿意,你可以返回一个指向数组的指针,并且你可以使用与使用数组相同的指针语法,只要使用下标(例如,*a
与 a[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];
,你认为ptr
和arr
具有相同的类型吗?仅仅因为数组很容易衰减为指针并不意味着它们是同一回事。【参考方案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();
将无法工作,因为没有为名为@987654328@ 的对象预留存储空间,与数组元素本身分开;没有什么可以分配给 。
【讨论】:
以上是关于为啥 C 函数不能返回数组类型? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
为啥我不能在 Swift 中将对象数组返回给 UIStackView? [复制]
为啥从函数返回数组作为参数时,我会从函数中的数组中获取随机值? [复制]