c - 为什么 C 中的数组会衰减为指针?

标签 c arrays pointers

[这是一个受最近其他地方的讨论启发而提出的问题,我会直接提供答案。]

我想知道数组“衰减”为指针的奇怪 C 现象,例如当用作函数参数时。那看起来太不安全了。显式传递长度也不方便。而且我可以按值完美地传递另一种类型的聚合——结构;结构不会衰减。

这个设计决定背后的基本原理是什么?它如何与语言结合?为什么结构有所不同?

最佳答案

基本原理

让我们检查一下函数调用,因为那里的问题很明显:为什么数组不简单地作为数组、按值、作为副本传递给函数?

首先有一个纯粹实用的原因:数组可以很大;按值(value)传递它们可能是不可取的,因为它们 可能会超过筹码量,尤其是在 1970 年代。第一个编译器是在具有大约 9 kB RAM 的 PDP-7 上编写的。

还有一个 Root 于语言的更技术性的原因。很难为编译时参数大小未知的函数调用生成代码。对于所有数组,包括现代 C 中的可变长度数组,只需将地址放在调用堆栈上。 地址的大小当然是众所周知的。即使是带有携带运行时大小信息的复杂数组类型的语言也不会在堆栈上正确传递对象。这些语言通常传递“句柄”,这也是 C 40 年来有效完成的事情。见乔恩双向飞碟 here以及他引用的图解说明(原文如此)here .

现在,一种语言可以要求数组始终具有完整的类型;即无论何时使用它,它的完整声明(包括大小)都必须是可见的。毕竟,这是 C 对结构的要求(当它们被访问时)。因此,结构可以按值传递给函数。要求数组的完整类型也将使函数调用易于编译,并避免传递额外长度参数的需要:sizeof() 仍将按预期在被调用方内部工作。但想象一下这意味着什么。如果大小确实是数组参数类型的一部分,我们将需要为每个数组大小使用不同的函数:

// for user input.
int average_ten(int arr[10]);

// for my new Hasselblad.
int average_twohundredfivemilliononehundredfourtyfivethousandsixhundred(int arr[16544*12400]);
// ...

事实上,它完全可以与传递结构相媲美,如果它们的元素不同,则类型不同(例如,一个结构有 10 个 int 元素,一个结构有 16544*12400)。很明显,数组需要更大的灵 active 。例如,正如所证明的那样,人们无法明智地提供采用数组参数的普遍可用的库函数。

这个“强类型难题”实际上是 C++ 中函数引用数组时发生的情况;这也是为什么没有人这样做的原因,至少没有明确地这样做。除了针对特定用途的情况和通用代码之外,它完全不方便以至于无用:C++ 模板提供了编译时的灵 active ,这在 C 中是不可用的。

如果在现有的 C 语言中,确实应该按值传递已知大小的数组,则始终可以将它们包装在结构中。我记得 Solaris 上一些与 IP 相关的 header 定义了地址族结构,其中包含数组,允许复制它们。因为结构的字节布局是固定的并且是已知的,所以这是有道理的。

对于一些背景知识,阅读丹尼斯·里奇 (Dennis Ritchie) 撰写的关于 C 语言起源的The Development of the C Language 也很有趣。C 的前身 BCPL 没有任何数组;内存只是带有指针的同构线性内存。

关于c - 为什么 C 中的数组会衰减为指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36417105/

相关文章:

c - 为什么在使用指针交换两个变量时会得到意想不到的结果?

c - 如何在 Codeblocks 中构建我的代码?Codeblocks IDE 停止工作

c++ - Char 数组指针返回不正确(当前年份 ctime)

c++ - C++ 中的 STL 列表指针

c++ - C++ 中标量初始值设定项错误的大括号

c++ - 赋值运算符重载期间类的指针成员

arrays - C 中数组的快速累积和

Python3和C不同的数学除法结果

java - 数组和Java字符串错误 : [Ljava. lang.String;@19c42c4b

java - 生成新字符串数组的输出未按预期输出