为什么 C 中的数组会衰减到指针? [英] Why do arrays in C decay to pointers?

查看:28
本文介绍了为什么 C 中的数组会衰减到指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

[这个问题的灵感来自于最近在别处的讨论,我会直接提供答案.]

[This is a question inspired by a recent discussion elsewhere, and I'll provide an answer right with it.]

我想知道数组衰减"到指针的奇怪 C 现象,例如当用作函数参数时.那看起来太不安全了.用它显式传递长度也很不方便.我可以传递另一种类型的聚合——结构——非常好地按值传递;结构不会衰减.

I was wondering about the odd C phenomenon of arrays "decaying" to pointers, e.g. when used as function arguments. That just seems so unsafe. It is also inconvenient to pass the length explicitly with it. And I can pass the other type of aggregate -- structs -- perfectly well by value; structs do not decay.

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

What is the rationale behind this design decision? How does it integrate with the language? Why is there a difference to structs?

推荐答案

基本原理

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

Let's examine function calls because the problems are nicely visible there: Why are arrays not simply passed to functions as arrays, by value, as a copy?

首先有一个纯粹实用的原因:数组可以很大;可能不建议按值传递它们,因为它们可能会超过堆栈大小,尤其是在 1970 年代.第一个编译器是在具有大约 9 kB RAM 的 PDP-7 上编写的.

There is first a purely pragmatic reason: Arrays can be big; it may not be advisable to pass them by value because they could exceed the stack size, especially in the 1970s. The first compilers were written on a PDP-7 with about 9 kB RAM.

还有一个更扎根于语言的技术原因.很难为参数大小在编译时未知的函数调用生成代码.对于所有数组,包括现代 C 中的可变长度数组,只需将地址放在调用堆栈上.地址的大小当然是众所周知的.即使具有携带运行时大小信息的复杂数组类型的语言也不会在堆栈上传递正确的对象.这些语言通常会传递句柄",这也是 C 40 年来有效完成的工作.请参阅 Jon Skeet 此处 以及他引用的插图说明(原文如此)这里.

There is also a more technical reason rooted in the language. It would be hard to generate code for a function call with arguments whose size is not known at compile time. For all arrays, including variable length arrays in modern C, simply the addresses are put on the call stack. The size of an address is of course well known. Even languages with elaborate array types carrying run time size information do not pass the objects proper on the stack. These languages typically pass "handles" around, which is what C has effectively done, too, for 40 years. See Jon Skeet here and an illustrated explanation he references (sic) here.

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

Now a language could make it a requirement that an array always have a complete type; i.e. whenever it is used, its complete declaration including the size must be visible. This is, after all, what C requires from structures (when they are accessed). Consequently, structures can be passed to functions by value. Requiring the complete type for arrays as well would make function calls easily compilable and obviate the need to pass additional length arguments: sizeof() would still work as expected inside the callee. But imagine what that means. If the size were really part of the array's argument type, we would need a distinct function for each array size:

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

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

事实上,它与传递结构完全可比,如果它们的元素不同,则它们的类型不同(例如,一个结构具有 10 个 int 元素,一个结构具有 16544*12400).很明显,数组需要更多的灵活性.例如,正如所展示的那样,我们无法明智地提供通常可用的接受数组参数的库函数.

In fact it would be totally comparable to passing structures, which differ in type if their elements differ (say, one struct with 10 int elements and one with 16544*12400). It is obvious that arrays need more flexibility. For example, as demonstrated one could not sensibly provide generally usable library functions which take array arguments.

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

This "strong typing conundrum" is, in fact, what happens in C++ when a function takes a reference to an array; that is also the reason why nobody does it, at least not explicitly. It is totally inconvenient to the point of being useless except for cases which target specific uses, and in generic code: C++ templates provide compile-time flexibility which is not available in C.

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

If, in existing C, indeed arrays of known sizes should be passed by value there is always the possibility to wrap them in a struct. I remember that some IP related headers on Solaris defined address family structures with arrays in them, allowing to copy them around. Because the byte layout of the struct was fixed and known, that made sense.

对于某些背景,阅读 Dennis Ritchie 撰写的关于 C 的起源的C 语言的发展 也很有趣.C 的前身 BCPL 没有任何数组;内存只是带有指向它的指针的同构线性内存.

For some background it's also interesting to read The Development of the C Language by Dennis Ritchie about the origins of C. C's predecessor BCPL didn't have any arrays; the memory was just homogeneous linear memory with pointers into it.

这篇关于为什么 C 中的数组会衰减到指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆