有些语言允许您像普通函数一样声明返回数组的函数,例如Java:
public String[] funcarray() {
String[] test = new String[]{"hi", "hello"};
return test;
}
为什么C++不支持像int[] funcarray(){}
这样的东西?
你可以返回一个数组,但是创建这样一个函数真的很麻烦。而且,我听说字符串只是char数组。那么如果在C++中可以返回一个字符串,为什么不能返回一个数组呢?有些语言允许您像普通函数一样声明返回数组的函数,例如Java:
public String[] funcarray() {
String[] test = new String[]{"hi", "hello"};
return test;
}
为什么C++不支持像int[] funcarray(){}
这样的东西?
你可以返回一个数组,但是创建这样一个函数真的很麻烦。而且,我听说字符串只是char数组。那么如果在C++中可以返回一个字符串,为什么不能返回一个数组呢?我猜测这只是一个设计决策,为了简洁明了。更具体地说,如果你真的想知道为什么,你需要从最基础的东西开始学习。
让我们先考虑C语言。在C语言中,“按引用传递”和“按值传递”的区别是非常明显的。简单来说,在C语言中,数组的名称实际上就是一个指针。就大部分情况而言,它们之间的区别在于内存分配。下面的代码
int array[n];
在32位系统上,这将创建4*n字节的内存(在声明所属的代码块的范围内)。
int* array = (int*) malloc(sizeof(int)*n);
会在堆上创建同样大小的内存。在这种情况下,内存中的内容与作用域无关,只有对内存的引用受到作用域的限制。这就是传值和传引用的区别。传值意味着当将某个东西传递给函数或从函数返回时,被传递的"东西"是变量求值的结果。换句话说,
int n = 4;
printf("%d", n);
代码将打印数字4,因为构造n
的值为4(如果这太基础了,我只是想涵盖所有情况)。这个4与程序的内存空间没有任何关系或联系,它只是一个字面上的值,一旦你离开拥有上下文的作用域,你就失去了它。那传递引用呢?在函数的上下文中,传递引用并没有什么不同;你只需评估被传递的构造。唯一的区别是,在评估传递的“东西”之后,你将评估结果作为内存地址使用。我曾经有一个特别愤世嫉俗的计算机科学教师,他喜欢说不存在传递引用,只有传递巧妙的值的方法。实际上,他是对的。所以现在我们从函数的角度来思考作用域。假设你可以有一个数组返回类型:
int[] foo(args){
result[n];
// Some code
return result;
}
这里的问题在于result被评估为数组的第0个元素的地址。但当你试图从该函数之外访问这个内存时(通过返回值),你会遇到一个问题,因为你正在尝试访问不在当前作用域中的内存(函数调用的堆栈)。所以我们通过标准的“按引用传递”技巧来解决这个问题:
int* foo(args){
int* result = (int*) malloc(sizeof(int)*n));
// Some code
return result;
}
在C++中,如果你想返回一个数组,你必须通过使用指针来手动管理内存。相比之下,在Java中,虽然语言会将数组或字符串版本转换为指针版本并自动处理内存,但这往往效率不高,因为它需要大量的内存分配和垃圾回收操作。
事实上,尽管Java中所有东西都是按值传递的,但几乎所有的值实际上都是内存地址。这意味着在Java中,即使你能够返回数组或字符串,它们仍然是被转换成指针版本返回的。C++之所以没有实现类似于Java的自动内存管理或垃圾回收机制,是因为其设计者Bjarne Stroustrup认为这样做会导致程序运行速度变慢。因此,在C++中,如果你想返回一个数组,你必须手动管理内存,而不能像Java那样让语言自动处理。
C++确实支持它——嗯,有点像:
vector< string> func()
{
vector<string> res;
res.push_back( "hello" );
res.push_back( "world" );
return res;
}
甚至C也有一定程度的支持:
struct somearray
{
struct somestruct d[50];
};
struct somearray func()
{
struct somearray res;
for( int i = 0; i < 50; ++i )
{
res.d[i] = whatever;
}
// fill them all in
return res;
}
std::string
是一个类,但当你说“字符串”时,你可能是指字面量。你可以从函数安全地返回字面量,但事实上,你可以静态创建任何数组并将其从函数返回。如果这是一个const(只读)数组,那么它就是线程安全的,这也是字符串字面量的情况。
但是,你返回的数组会退化成指针,所以你无法仅从返回值中得知它的大小。
如果可能的话,返回一个数组首先必须是固定长度的,因为编译器需要创建调用堆栈,然后存在这样的问题:数组不是L-value,所以在调用函数中接收它的方式是使用具有初始化的新变量,这是不切实际的。由于相同的原因,返回一个数组也可能是不切实际的,尽管它们可能已经使用了一种特殊的符号表示法来表示返回值。
请记住,在C的早期,所有变量都必须在函数顶部声明,你不能只在使用时声明。因此,当时是行不通的。
他们给出了将数组放入结构体中的解决方法,而这正是在C++中必须保持的方式,因为它使用相同的调用约定。
注意:在像Java这样的语言中,数组是一个类。你可以使用new来创建它们,也可以重新分配它们(它们是L-value)。
无法从函数返回数组,因为该数组将在函数内部声明,并且其位置将是堆栈帧。但是,当函数退出时,堆栈帧会被擦除。函数必须将返回值从堆栈帧复制到返回位置,而对于数组来说这是不可能的。
来自这里的讨论:
http://forum.codecall.net/c-c/32457-function-return-array-c.html
有人说在C++中,使用vector<>而不是从C继承的数组。
那么为什么C++不允许返回C数组呢?因为C语言也不允许。
为什么C语言不允许呢?因为C语言是从一种无类型语言B演变而来的,而在B语言中返回一个数组根本没有意义。在给B语言添加类型时,可以使返回数组成为可能,但这并没有做到,以保持某些B语言习惯用法的有效性,并简化从B到C的程序转换。自那时以来,使C数组更易于使用的可能性一直被拒绝(甚至没有考虑),因为它会破坏太多现有的代码。
int[4] f() { int x[4]; ...populate x...; return x; }
为了使其以直观的方式有用,让我们添加一个新的支持要求,即在返回值和 int x[4] = f();
中都需要对数组进行赋值。我不认为这需要指针衰减,也不需要更改其他代码来防止指针衰减。您看到哪些代码与此冲突? - Tony Delroyint (*p)[4]
一样,*p
会衰减为指针)。 - AProgrammerlong x = get_char();
一样 - 转换为 long
仅在赋值的右操作数不是 long
时尝试。因此,我们所讨论的不是指针衰变的抑制,而是在考虑指针衰变之前使某些新东西起作用。"(就像 int (p)[4],p 衰变为指针)" - 不是这样的,*p
仍然是 int[4]
- 通过传递给 template <int N> void f(int (&a)[N]) { std::cout << N << '\n'; }
进行确认。衰变是最后的手段。 - Tony Delroypublic std::string* funcarray() {
std::string* test = new std::string[2];
test[0] = "hi";
test[1] = "hello";
return test;
}
// somewhere else:
std::string* arr = funcarray();
std::cout << arr[0] << " MisterSir" << std::endl;
delete[] arr;
或者您可以直接使用std命名空间中的容器,如std::vector。
test
是一个存储在堆栈上的变量,在函数返回时会超出作用域。然而,test
指向的位置位于堆/自由存储器上,并返回给arr
。因此,如果您删除arr
,就足够了。 - Maheshstd::array
部分是为了克服这种古怪的限制。这与 GC 或引用无关。C++ 允许按值返回对象(事实上 C 也是如此)。只是不允许返回普通数组。你的回答毫无意义。 - juanchopanza应该返回一个 std::vector<>
而不是一个数组。一般来说,数组在 C++ 中表现不佳,应尽量避免使用。
string
数据类型不仅仅是字符数组,虽然 "quoted string" 是。 string
管理一个字符数组,并且您可以使用 .c_str()
访问它,但是 string
不止于此。
这些答案都没有抓住重点。C++根本不支持它。甚至在std::array<T, N>
之前,它甚至不支持返回静态大小的数组的方法。C++可以支持返回动态大小的数组,但它们没有。我相信有可辩解的原因,但他们可以。
你需要做的就是在堆栈上分配动态数组,返回其地址和大小,并确保调用者将堆栈指针提升到返回的数组的末尾。可能需要一些堆栈帧修复,但绝不是不可能的。