Hud*_*den 34 c++ arrays pointers pass-by-reference
为什么不能将数组作为函数参数传递?
我一直在阅读这本C++书,上面写着"你不能将数组作为函数参数传递",但它从未解释过为什么.此外,当我在网上查找时,我发现了一些评论,比如'你为什么要这样做?' 这不是我会这样做的,我只是想知道为什么你不能这样做.
Lig*_*ica 45
为什么数组不能作为函数参数传递?
他们能:
void foo(const int (&myArray)[5]) {
// `myArray` is the original array of five integers
}
Run Code Online (Sandbox Code Playgroud)
在技术术语中,参数的类型foo
是"引用5s数组const
int
"; 通过引用,我们可以传递实际的对象(免责声明:术语因抽象级别而异).
你不能做的是传递价值,因为历史原因我们不会复制数组.相反,尝试按值将数组传递给函数(或传递数组的副本)会导致其名称衰减为指针.(有些资源弄错了!)
这意味着:
void foo(int* ptr);
int ar[10]; // an array
foo(ar); // automatically passing ptr to first element of ar (i.e. &ar[0])
Run Code Online (Sandbox Code Playgroud)
还有一个极具误导性的"语法糖",看起来你可以按值传递一个任意长度的数组:
void foo(int ptr[]);
int ar[10]; // an array
foo(ar);
Run Code Online (Sandbox Code Playgroud)
但是,实际上,你仍然只是传递一个指针(到第一个元素ar
).foo
和上面一样!
虽然我们正在使用它,但以下功能也没有真正具有它的签名.看看当我们尝试调用此函数而不定义它时会发生什么:
void foo(int ar[5]);
int main() {
int ar[5];
foo(ar);
}
// error: undefined reference to `func(int*)'
Run Code Online (Sandbox Code Playgroud)
因此,foo
需要int*
在实际上,不 int[5]
!
(现场演示.)
您可以通过将数组包装在struct
or中来解决此问题class
,因为默认的复制操作符将复制数组:
struct Array_by_val
{
int my_array[10];
};
void func (Array_by_val x) {}
int main() {
Array_by_val x;
func(x);
}
Run Code Online (Sandbox Code Playgroud)
这有点令人困惑的行为.
在C++中,通过一些模板魔术,我们可以使一个函数既可重用又能够接收数组:
template <typename T, size_t N>
void foo(const T (&myArray)[N]) {
// `myArray` is the original array of N Ts
}
Run Code Online (Sandbox Code Playgroud)
但我们仍然无法通过价值传递.要记住的东西.
而且由于C++ 11刚刚开始,并且C++ 0x支持在主流工具链中得到很好的支持,你可以使用std::array
继承自Boost 的可爱!我会把研究作为练习留给读者.
Die*_*Epp 14
所以我看到答案解释说:"为什么编译器不允许我这样做?" 而不是"是什么导致标准指定这种行为?" 答案在于C的历史.这取自Dennis Ritchie的"C语言的发展"(来源).
在原型C语言中,存储器被分成"单元",每个单元包含一个单词.这些可以使用最终的一元运算*
符取消引用- 是的,这些基本上是无类型的语言,就像今天的一些玩具语言,如Brainf_ck.语法糖允许一个假装指针是一个数组:
a[5]; // equivalent to *(a + 5)
Run Code Online (Sandbox Code Playgroud)
然后,添加了自动分配:
auto a[10]; // allocate 10 cells, assign pointer to a
// note that we are still typeless
a += 1; // remember that a is a pointer
Run Code Online (Sandbox Code Playgroud)
在某些时候,auto
存储说明符行为变为默认 - 您可能也想知道auto
关键字的重点是什么,就是这样.由于这些增量变化,指针和数组的行为有些古怪.如果语言是从鸟瞰图设计的,那么这些类型的行为可能更相似.就目前而言,这只是一个C/C++问题.
从某种意义上说,数组是第二类,C++继承自C语言.
引用C99标准中的 6.3.2.1p3 :
除了当它是的操作数的sizeof操作者或一元&运算,或者是用于初始化一个数组,其具有输入"的阵列的表达一个字符串文字类型 "被转换为与类型"指针到表达型 "即指向数组对象的初始元素,而不是左值.如果数组对象具有寄存器存储类,则行为未定义.
C11标准中的相同段落基本相同,增加了新的_Alignof
运算符.(两个链接都是非常接近官方标准的草稿.(更新:这实际上是N1570草案中的一个错误,在已发布的C11标准中得到纠正.)_Alignof
不能应用于表达式,只能应用于带括号的类型名称,所以C11只有C99和C90所做的3个例外.(但我离题了.)))
我没有相应的C++引用,但我相信它非常相似.
因此,如果arr
是一个数组对象,并且你调用一个函数func(arr)
,那么func
将接收一个指向第一个元素的指针arr
.
到目前为止,这或多或少"它以这种方式工作,因为它以这种方式定义",但有历史和技术原因.
允许数组参数不会允许很大的灵活性(没有对语言的进一步更改),因为,例如,char[5]
并且char[6]
是不同的类型.即使通过引用传递数组也没有用(除非我缺少一些C++特性,总是有可能).传递指针会给你极大的灵活性(也许太多了!).指针可以指向任何大小的数组的第一个元素 - 但是你必须滚动自己的机制来告诉函数数组有多大.
设计一种语言,使不同长度的数组在某种程度上兼容,同时仍然是不同的,实际上非常棘手.例如,在Ada中,等价的char[5]
和char[6]
是相同的类型,但是不同的子类型.更多动态语言使长度成为数组对象值的一部分,而不是其类型.C仍然非常混乱,显式指针和长度,或指针和终结符.C++从C继承了所有这些包袱.它主要是对整个数组的事情进行了抨击并引入了向量,因此没有太多需要使数组成为一流的类型.
TL; DR:这是C++,你应该使用矢量!(好吧,有时候.)
归档时间: |
|
查看次数: |
9124 次 |
最近记录: |