为什么数组不能作为函数参数传递?
为什么不能将数组作为函数参数传递?
我一直在读这本 C++ 书,其中说“你不能将数组作为函数参数传递”,但它从未解释原因。另外,当我在网上查找时,我发现了诸如“你为什么要这样做?”之类的评论。并不是我愿意做,我只是想知道为什么你做不到。
Why can't you pass arrays as function arguments?
I have been reading this C++ book that says 'you can't pass arrays as function arguments', but it never explains why. Also, when I looked it up online I found comments like 'why would you do that anyway?' It's not that I would do it, I just want to know why you can't.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
他们可以:
用技术术语来说,
foo
的参数类型是“对 5 个const
int
数组的引用”;通过引用,我们可以传递实际对象(免责声明:术语因抽象级别而异)。您不能做的是按值传递,因为由于历史原因,我们不会复制数组。相反,尝试按值将数组传递给函数(或者传递数组的副本)会导致其名称退化为指针。 (某些资源出现此错误!)< /em>
数组名称衰减为按值传递的指针
这意味着:
还有一种极具误导性的“语法糖”,看起来就像您可以按值传递任意长度的数组:
但是,实际上,你还只是传递一个指针(指向 ar 的第一个元素)。
foo
与上面相同!当我们这样做时,下面的函数也并没有真正具有它看起来的签名。看看当我们尝试调用这个函数而不定义它时会发生什么:
所以
foo
实际上采用int*
,不是int[5 ]
!(现场演示。)
但是您可以解决这个问题!
您可以通过将数组包装在
struct
或class< 中来解决这个问题
/code>,因为默认的复制运算符将复制数组:
这是有点令人困惑的行为。
或者,更好的是,一种通用的按引用传递方法
在 C++ 中,借助一些模板魔法,我们可以使函数既可重用又能够接收数组:
但我们仍然无法按值传递。需要记住的事情。
未来...
由于 C++11 即将出现,并且 C++0x 支持在主流工具链中进展顺利,您可以使用继承自的可爱的
std::array
促进!我将把这个研究作为练习留给读者。They can:
In technical terms, the type of the argument to
foo
is "reference to array of 5const
int
s"; with references, we can pass the actual object around (disclaimer: terminology varies by abstraction level).What you can't do is pass by value, because for historical reasons we shall not copy arrays. Instead, attempting to pass an array by value into a function (or, to pass a copy of an array) leads its name to decay into a pointer. (some resources get this wrong!)
Array names decay to pointers for pass-by-value
This means:
There's also the hugely misleading "syntactic sugar" that looks like you can pass an array of arbitrary length by value:
But, actually, you're still just passing a pointer (to the first element of
ar
).foo
is the same as it was above!Whilst we're at it, the following function also doesn't really have the signature that it seems to. Look what happens when we try to call this function without defining it:
So
foo
takesint*
in fact, notint[5]
!(Live demo.)
But you can work-around it!
You can hack around this by wrapping the array in a
struct
orclass
, because the default copy operator will copy the array:This is somewhat confusing behaviour.
Or, better, a generic pass-by-reference approach
In C++, with some template magic, we can make a function both re-usable and able to receive an array:
But we still can't pass one by value. Something to remember.
The future...
And since C++11 is just over the horizon, and C++0x support is coming along nicely in the mainstream toolchains, you can use the lovely
std::array
inherited from Boost! I'll leave researching that as an exercise to the reader.所以我看到答案解释说:“为什么编译器不允许我这样做?”而不是“是什么导致标准指定了这种行为?”答案就在C的历史中。这摘自《C语言的发展》(来源)作者:丹尼斯·里奇。
在原始 C 语言中,内存被划分为“单元”,每个单元包含一个单词。这些可以使用最终的一元
*
运算符来取消引用 - 是的,这些本质上是无类型语言,就像当今的一些玩具语言(如 Brainf_ck)一样。语法糖允许人们假装指针是一个数组:然后,添加了自动分配:
在某些时候,
auto
存储说明符行为成为默认行为 - 您可能还想知道 < 的意义是什么code>auto 关键字无论如何,就是这样。由于这些增量变化,指针和数组的行为方式有些奇怪。如果语言是从鸟瞰角度设计的,也许这些类型的行为会更加相似。就目前情况而言,这只是 C/C++ 的又一陷阱。So I see answers explaining, "Why doesn't the compiler allow me to do this?" Rather than "What caused the standard to specify this behavior?" The answer lies in the history of C. This is taken from "The Development of the C Language" (source) by Dennis Ritchie.
In the proto-C languages, memory was divided into "cells" each containing a word. These could be dereferenced using the eventual unary
*
operator -- yes, these were essentially typeless languages like some of today's toy languages like Brainf_ck. Syntactic sugar allowed one to pretend a pointer was an array:Then, automatic allocation was added:
At some point, the
auto
storage specifier behavior became default -- you may also be wondering what the point of theauto
keyword was anyway, this is it. Pointers and arrays were left to behave in somewhat quirky ways as a result of these incremental changes. Perhaps the types would behave more alike if the language were designed from a bird's-eye view. As it stands, this is just one more C / C++ gotcha.数组在某种意义上是二等类型,是 C++ 从 C 继承的。
在 C99 标准:
C11 标准中的同一段落是本质上是相同的,只是添加了新的
_Alignof
运算符。 (这两个链接都是非常接近官方标准的草案。(更新:这实际上是 N1570 草案中的一个错误,已在发布的 C11 标准中更正。_Alignof
不能应用于表达式,只能应用于带括号的类型名称,因此 C11 只有与 C99 和 C90 相同的 3 个例外(但我离题了。)))我没有相应的 C++ 引用很方便,但我相信它非常相似。
因此,如果
arr
是一个数组对象,并且您调用函数func(arr)
,则func
将收到指向第一个元素的指针到达
。到目前为止,这或多或少是“它以这种方式工作,因为它是这样定义的”,但其中有历史和技术原因。
允许数组参数不会带来太大的灵活性(无需进一步更改语言),因为例如
char[5]
和char[6]
是不同的类型。即使通过引用传递数组也无济于事(除非我缺少一些 C++ 功能,但总是有可能)。传递指针给你带来了巨大的灵活性(也许太多了!)。指针可以指向任意大小的数组的第一个元素——但是您必须使用自己的机制来告诉函数数组有多大。设计一种语言,使不同长度的数组在某种程度上兼容,同时仍然不同,实际上是相当棘手的。例如,在 Ada 中,
char[5]
和char[6]
的等价物是相同的类型,但不同的子类型。更多动态语言将长度作为数组对象值的一部分,而不是其类型的一部分。 C 仍然很混乱地使用显式指针和长度,或者指针和终止符。 C++ 继承了 C 的所有包袱。它主要把精力放在整个数组上,并引入了向量,因此没有那么多需要使数组成为一流类型。TL;DR:这是 C++,无论如何你都应该使用向量! (嗯,有时。)
Arrays are in a sense second-class types, something that C++ inherited from C.
Quoting 6.3.2.1p3 in the C99 standard:
The same paragraph in the C11 standard is essentially the same, with the addition of the new
_Alignof
operator. (Both links are to drafts which are very close to the official standards. (UPDATE: That was actually an error in the N1570 draft, corrected in the released C11 standard._Alignof
can't be applied to an expression, only to a parenthesized type name, so C11 has only the same 3 exceptions that C99 and C90 did. (But I digress.)))I don't have the corresponding C++ citation handy, but I believe it's quite similar.
So if
arr
is an array object, and you call a functionfunc(arr)
, thenfunc
will receive a pointer to the first element ofarr
.So far, this is more or less "it works that way because it's defined that way", but there are historical and technical reasons for it.
Permitting array parameters wouldn't allow for much flexibility (without further changes to the language), since, for example,
char[5]
andchar[6]
are distinct types. Even passing arrays by reference doesn't help with that (unless there's some C++ feature I'm missing, always a possibility). Passing pointers gives you tremendous flexibility (perhaps too much!). The pointer can point to the first element of an array of any size -- but you have to roll your own mechanism to tell the function how big the array is.Designing a language so that arrays of different lengths are somewhat compatible while still being distinct is actually quite tricky. In Ada, for example, the equivalents of
char[5]
andchar[6]
are the same type, but different subtypes. More dynamic languages make the length part of an array object's value, not of its type. C still pretty much muddles along with explicit pointers and lengths, or pointers and terminators. C++ inherited all that baggage from C. It mostly punted on the whole array thing and introduced vectors, so there wasn't as much need to make arrays first-class types.TL;DR: This is C++, you should be using vectors anyway! (Well, sometimes.)
数组不是按值传递的,因为数组本质上是连续的内存块。如果您有一个想要按值传递的数组,则可以在结构中声明它,然后通过该结构访问它。
这本身就会对性能产生影响,因为这意味着您将在堆栈上锁定更多空间。传递指针速度更快,因为要复制到堆栈上的数据包络要少得多。
Arrays are not passed by value because arrays are essentially continuous blocks of memmory. If you had an array you wanted to pass by value, you could declare it within a structure and then access it through the structure.
This itself has implications on performance because it means you will lock up more space on the stack. Passing a pointer is faster because the envelope of data to be copied onto the stack is far less.
这是因为技术原因。参数在堆栈上传递;数组可以有很大的大小,兆字节甚至更多。每次调用时将数据复制到堆栈不仅会变慢,而且会很快耗尽堆栈。
您可以通过将数组放入结构中(或使用 Boost::Array)来克服该限制:
尝试对该函数进行嵌套调用,看看会出现多少堆栈溢出!
It's because of a technical reason. Arguments are passed on the stack; an array can have a huge size, megabytes and more. Copying that data to the stack on every call will not only be slower, but it will exhaust the stack pretty quickly.
You can overcome that limitation by putting an array into a struct (or using Boost::Array):
Try to make nested calls of that function and see how many stack overflows you'll get!
我相信C++这样做的原因是,当它被创建时,它可能占用了太多的资源来发送整个数组而不是内存中的地址。这只是我对此事的想法和假设。
I believe that the reason why C++ did this was, when it was created, that it might have taken up too many resources to send the whole array rather than the address in memory. That is just my thoughts on the matter and an assumption.