有些语言可以让你只声明一个像普通函数一样返回数组的函数,比如Java:
public String[] funcarray() {
String[] test = new String[]{"hi", "hello"};
return test;
}
为什么C ++不支持int[] funcarray(){}
之类的东西?
你可以返回一个数组,但是制作这样一个函数真的很麻烦。而且,我听说字符串只是char的数组。所以如果你能用C ++返回一个字符串,为什么不用数组?
答案 0 :(得分:59)
我猜想要简明扼要,这只是一个设计决定。更具体地说,如果你真的想知道原因,你需要从头开始工作。
让我们先考虑一下C。在C语言中,“按引用传递”和“按值传递”之间存在明显的区别。为了轻视它,C中数组的名称实际上只是一个指针。对于所有意图和目的,差异(通常)归结为分配。代码
int array[n];
将在堆栈上创建4 * n字节的内存(在32位系统上),与发布声明的代码块的范围相关。反过来,
int* array = (int*) malloc(sizeof(int)*n);
会创建相同数量的内存,但是在堆上。在这种情况下,该内存中的内容与范围无关,只有内存的引用受范围的限制。这里是按值传递和按引用传递的地方。正如您可能知道的那样,传递值意味着当某个东西传递给函数或从函数返回时,传递的“东西”是评估变量的结果。换句话说,
int n = 4;
printf("%d", n);
将打印数字4,因为构造n
评估为4(对不起,如果这是基本的,我只想覆盖所有基础)。这4与你的程序的内存空间完全没有关系或关系,它只是一个文字,所以一旦你离开了4具有上下文的范围,你就会失去它。通过参考传递怎么样?通过引用传递在函数的上下文中没有区别;您只需评估传递的构造。唯一的区别是在评估传递的“事物”之后,您将评估结果用作内存地址。我曾经有一个特别愤世嫉俗的CS导师,他喜欢说没有通过引用传递这样的东西,只是一种传递聪明价值的方法。真的,他是对的。所以现在我们考虑功能方面的范围。假装你可以有一个数组返回类型:
int[] foo(args){
result[n];
// Some code
return result;
}
这里的问题是结果计算到数组的第0个元素的地址。但是当您尝试从此函数外部(通过返回值)访问此内存时,您遇到了问题,因为您正在尝试访问不在您工作范围内的内存(函数调用的堆栈)。因此,我们解决这个问题的方式是使用标准的“传递参考”jiggery-pokery:
int* foo(args){
int* result = (int*) malloc(sizeof(int)*n));
// Some code
return result;
}
我们仍然得到一个指向数组第0个元素的内存地址,但现在我们可以访问该内存了。
我的观点是什么?在Java中,通常断言“一切都是通过价值传递”。这是真的。上面同样的愤世嫉俗的教师也对Java和OOP有这样的说法:一切都只是一个指针。他也是对的。虽然Java中的所有内容实际上都是按值传递的,但几乎所有这些值实际上都是内存地址。所以在Java中,该语言确实允许您返回数组或字符串,但它通过将其转换为带有指针的版本来实现。它还为您管理您的记忆。而自动内存管理虽然有用但效率不高。
这将我们带到了C ++。 C ++被发明的全部原因是因为Bjarne Stroustrup在他的博士工作期间一直在试验Simula(基本上是原始的OOPL),并且认为它在概念上很棒,但他注意到它的表现非常糟糕。所以他开始研究C with Classes,它被重命名为C ++。在这样做的过程中,他的目标是制作一种编程语言,该语言采用了Simula的一些最佳功能,但仍然保持强大和快速。由于其已经具有传奇色彩的表现,他选择扩展C,并且一个权衡是他选择不像其他OOPL那样大规模地实现自动内存管理或垃圾收集。从其中一个模板类返回一个数组是有效的,因为你正在使用一个类。但是如果你想返回一个C数组,你必须以C方式进行。换句话说,C ++确实支持以与Java相同的方式返回数组;它只是没有为你做所有的工作。因为一个丹麦人认为它太慢了。
答案 1 :(得分:31)
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值的问题,因此接收它调用函数必须使用带有初始化的新变量,这是不切实际的。由于同样的原因,返回一个也可能是不切实际的,尽管它们可能对返回值使用了特殊的符号。
请记住,在C的早期,所有变量都必须在函数的顶部声明,并且您不能在第一次使用时声明。因此,当时这是不可行的。
他们提供了将数组放入结构的解决方法,这就是它现在必须保留在C ++中的原因,因为它使用相同的调用约定。
注意:在Java等语言中,数组是一个类。你创建一个新的。你可以重新分配它们(它们是l值)。
答案 2 :(得分:26)
C中的数组(以及C ++中的向后兼容性)具有与其他类型不同的特殊语义。特别是,对于其余类型,C只具有按值传递的语义,在数组的情况下,传值语法的效果以奇怪的方式模拟传递引用:
在函数签名中,类型为的N个元素的数组的参数转换为指向T 的指针。在函数调用中,将数组作为参数传递给函数将将数组衰减指向第一个元素的指针,并将该指针复制到函数中。
由于对阵列的这种特殊处理 - 它们不能通过值传递 - 它们也不能通过值返回。在C中你可以返回一个指针,在C ++中你也可以返回一个引用,但是数组本身不能在栈中分配。
如果你想到它,这与你在问题中使用的语言没有什么不同,因为数组是动态分配的,你只返回一个指针/引用。
另一方面,C ++语言为该特定问题提供了不同的解决方案,例如在当前标准中使用std::vector
(内容是动态分配的)或在即将推出的标准中使用std::array
(内容可以在堆栈中分配,但它可能会有更高的成本,因为在编译器无法省略副本的情况下,必须复制每个元素。实际上,您可以使用与boost::array
等现成的库一样使用当前标准的相同类型的方法。
答案 3 :(得分:8)
“你无法从中返回数组 函数,因为该数组将是 在函数内部声明,以及它 然后位置将是堆栈 帧。但是,堆栈帧被擦除 当功能退出时。功能必须 将堆栈帧的返回值复制到 返回位置,但事实并非如此 可以使用数组。“
来自此处的讨论:
http://forum.codecall.net/c-c/32457-function-return-array-c.html
答案 4 :(得分:7)
其他人已经说过,在C ++中,一个人使用向量&lt;&gt;而不是从C继承的数组。
那么为什么C ++不允许返回C数组呢?因为C没有。
为什么C没有?因为C是从B演化而来的,这是一种无类型语言,其中返回数组根本没有意义。当向B添加类型时,使得返回数组成为可能是有意义的但是为了保持一些B成语有效并且使程序从B转换为C而没有这样做。从那以后,可能性使C数组更有用,因为它总是被拒绝(甚至更多,甚至不被考虑)因为它会破坏太多的现有代码。
答案 5 :(得分:3)
您可以返回指向数组的指针。稍后要小心释放内存。
public 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。
答案 6 :(得分:2)
“为什么C ++不支持”:因为它没有任何意义。在基于引用的语言(如JAVA或PHP)中,内存管理基于垃圾收集。没有引用的内存部分(程序中没有变量指向它)将自动释放。在这种情况下,您可以分配内存,并无忧无虑地传递引用。
C ++代码将被转换为机器代码,并且没有定义GC。所以在C和C ++中,存在强烈的所有权内存块的感觉。你必须知道你所使用的指针是否随时可以释放(实际上你在使用后 shoud 释放它),或者你有一个指向内存共享部分的指针,这是绝对的禁止自由。
在这种环境中,每次传递到函数和从函数传递数据时,您都无法获得数组的无限副本。使用类似c语言管理数据数组是一项非常复杂的任务。没有一个通用的解决方案,您需要知道何时释放内存。
函数返回的数组是一个副本(你是免费的)还是你必须复制它们?通过获取指向数组的指针数组,你会获胜吗?
答案 7 :(得分:1)
返回std::vector<>
而不是数组。通常,数组与C ++不兼容,通常应该避免使用。
此外,string
数据类型不仅仅是一个字符数组,尽管“带引号的字符串”是。 string
管理一系列字符,您可以使用.c_str()
访问该字符,但string
还有更多字符。
答案 8 :(得分:1)
点击这里。真有帮助。
答案 9 :(得分:0)
这些答案都没有抓住重点。 C ++不支持它。它甚至不支持在std::array<T, N>
之前返回静态大小的数组的方法。 C ++ 可以支持甚至返回动态大小的数组,但它们不支持。我敢肯定有合理的理由,但是可以。
所有您需要做的就是在堆栈上分配动态数组,返回它的地址和大小,并确保调用者将堆栈指针增加到返回数组的末尾。可能需要进行一些堆栈框架固定,但绝不是不可能的。