为什么我们不能传递数组按值运行?
Why can't we pass arrays to function by value?
显然,我们可以将复杂的类实例传递给函数,但为什么不能将数组传递给函数呢?
起源于历史。问题是"数组在传递给函数时会衰减为指针"的规则很简单。
复制数组有点复杂,而且不太清楚,因为不同的参数和不同的函数声明会改变行为。
请注意,您仍然可以通过值进行间接传递:
struct A { int arr[2]; };
void func(struct A);
这里有另一个视角:C中没有单一类型的"数组"。相反,对于每个N
,T[N]
都是一个不同的类型。因此,T[1]
、T[2]
等都是不同的类型。
在C中没有函数重载,因此您可以允许的唯一合理的事情是使用(或返回(单一类型数组的函数:
void foo(int a[3]); // hypothetical
据推测,这只是被认为远不如实际决定有用,即让所有数组衰减为指向第一个元素的指针,并要求用户通过其他方式传达大小。毕竟,以上内容可以重写为:
void foo(int * a)
{
static const unsigned int N = 3;
/* ... */
}
因此,表达能力没有损失,但在普遍性方面有了巨大的收获。
请注意,这在C++中没有任何不同,但模板驱动的代码生成允许您编写一个模板化函数foo(T (&a)[N])
,其中N
是为您推导的——但这只是意味着您可以创建一个完整的不同的函数家族,N
的每个值都有一个。
在极端情况下,假设您需要两个函数print6(const char[6])
和print12(const char[12])
,即print6("Hello")
和print12("Hello World")
,如果您不想将数组衰减为指针,或者您必须添加显式转换print_p((const char*)"Hello World")
回答一个非常古老的问题,因为问题是市场,C++只是为了完成而添加,我们可以使用std::array并通过值或引用将数组传递给函数,从而防止访问越界索引:
以下是示例:
#include <iostream>
#include <array>
//pass array by reference
template<size_t N>
void fill_array(std::array<int, N>& arr){
for(int idx = 0; idx < arr.size(); ++idx)
arr[idx] = idx*idx;
}
//pass array by value
template<size_t N>
void print_array(std::array<int, N> arr){
for(int idx = 0; idx < arr.size(); ++idx)
std::cout << arr[idx] << std::endl;
}
int main()
{
std::array<int, 5> arr;
fill_array(arr);
print_array(arr);
//use different size
std::array<int, 10> arr2;
fill_array(arr2);
print_array(arr2);
}
不能按值传递数组的原因是没有特定的方法来跟踪数组的大小,这样函数调用逻辑就会知道要分配多少内存以及要复制什么。您可以传递类实例,因为类具有构造函数。数组没有。
汇总:
- 传递数组第一个元素的地址
&a = a = &(a[0])
- 新指针(新指针,内存中的新地址,4字节(
- 指向相同的内存位置,在不同类型中
示例1:
void by_value(bool* arr) // pointer_value passed by value
{
arr[1] = true;
arr = NULL; // temporary pointer that points to original array
}
int main()
{
bool a[3] = {};
cout << a[1] << endl; // 0
by_value(a);
cout << a[1] << endl; // 1 !!!
}
地址:
[main]
a = 0046FB18 // **Original**
&a = 0046FB18 // **Original**
[func]
arr = 0046FB18 // **Original**
&arr = 0046FA44 // TempPTR
[func]
arr = NULL
&arr = 0046FA44 // TempPTR
示例2:
void by_value(bool* arr)
{
cout << &arr << arr; // &arr != arr
}
int main()
{
bool a[3] = {};
cout << &a << a; // &a == a == &a[0]
by_value(arr);
}
地址
Prints:
[main] 0046FB18 = 0046FB18
[func] 0046FA44 != 0046FB18
请注意:
- &(必需的左值(:左值-到->右值
- 数组衰退:新指针(临时(指向(按值(数组地址
自述:
R值
阵列衰减
这样做是为了保持与B语言的语法和语义兼容性,在B语言中,数组被实现为物理指针。
这个问题的直接答案在Dennis Ritchie的"C语言的发展"中给出,见"评论家"部分。上面写着
例如,函数声明中的空方括号
int f(a) int a[]; { ... }
是活化石,是NB宣布指针的方式的残余;仅在这种特殊情况下,
a
在C中被解释为指针。这种表示法之所以保留下来,部分是为了兼容性,部分是因为它将允许程序员向读者传达一种意图,即向f
传递一个由数组生成的指针,而不是对单个整数的引用。不幸的是,它既能提醒读者,也能迷惑学习者。
这应该放在文章前一部分的上下文中,特别是"胚胎C",它解释了在C中引入struct
类型是如何导致拒绝B和BCPL风格的方法来实现数组(即作为普通指针(的。C切换到非指针数组实现,只在函数参数列表中保留传统的B风格语义。
因此,数组参数行为的当前变体是一种折衷的结果:一方面,我们必须在struct
中拥有可复制的数组,另一方面,我方希望保持与用B编写的函数的语义兼容性,在B中,数组总是"通过指针"传递的。
等效的方法是首先复制数组,然后将其传递给函数(对于大型数组来说,这可能效率很低(。
除此之外,我想说这是由于历史原因,即不能通过C.中的值传递数组
我的猜测是,在C++中不引入按值传递数组的原因是,与数组相比,对象被认为是中等大小的。
正如delnan所指出的,当使用std::vector
时,实际上可以通过值将类似数组的对象传递给函数。
您正在传递值:指向数组的指针的值。请记住,在C中使用方括号表示法只是取消引用指针的简写。ptr[2]表示*(ptr+2(。
去掉括号可以得到一个指向数组的指针,该指针可以通过值传递给函数:
int x[2] = {1, 2};
int result;
result = DoSomething(x);
请参阅ANSI C规范中的类型列表。数组不是基元类型,而是由指针和运算符的组合构建的。(它不会让我再放一个链接,但结构在"数组类型派生"下描述。(
实际上,指向数组的指针是通过值传递的,在调用的函数中使用该指针会给你一种数组是通过引用传递的感觉,这是错误的。尝试更改数组指针中的值以指向函数中的另一个数组,您会发现原始数组没有受到影响,这意味着该数组不是通过引用传递的。
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 代码使用向量成功运行,但使用数组显示错误
- 给定一个整数数组,需要在Max_Heap上运行操作。得到错误"segmentation fault",有什么想法吗?(C++)
- 在 c++ 中是否允许创建具有运行时边界的数组?
- 运行时C++数组初始化问题
- 如何在运行时在对象数组中动态追加新对象C++并打印它们
- 如何在 Visual Studio C++ 11 中运行时定义二维数组?
- 传递数组结构、ofstream 和 interger 以运行
- c++:运行一个函数 8 次,并将每个答案添加到数组或 JSON 中
- 有没有办法根据命令行参数定义数组大小?运行时与编译时实例化?
- C++ .我正在尝试通过使用开关内的数组来获取用户输入,但是当我运行代码时,它显示分段错误?
- 如何在运行时阻止数组重置
- 运行搜索数组
- 当数组大小在C++中未知时,如何在运行时将对字符串数组的引用作为函数参数传递?
- 程序在初始化期间未与数组一起运行
- 由于 2D 数组声明,C++ 14 中的运行时错误
- 为什么C++允许在运行时将数组大小传递给函数以构造固定大小的数组?
- 在函数中创建的数组,编译时还是运行时?
- 在运行时 c++ 更改用类对象填充的数组的大小
- 尝试运行数组strcpy程序时需要帮助