为什么C ++不支持函数返回数组?
有些语言可以让你声明一个返回数组的函数,就像普通的函数一样:
public String[] funcarray() { String[] test = new String[]{"hi", "hello"}; return test; }
为什么C ++不支持int[] funcarray(){}
? 你可以返回一个数组,但是做这个函数真的很麻烦。 另外,我听说string只是char数组。 所以,如果你可以在C ++中返回一个string,为什么不是一个数组呢?
我敢打赌,简单来说,这只是一个devise决定。 更具体地说,如果你真的想知道为什么,你需要从头开始工作。
我们先来看C吧。 在C语言中,“通过引用”和“按价值传递”有明显的区别。 为了轻松处理它,C中数组的名称实际上只是一个指针。 对于所有意图和目的,差异(通常)归结为分配。 代码
int array[n];
会在堆栈上创build4 * n字节的内存(在32位系统上),与任何代码块进行声明的范围相关。 反过来,
int* array = (int*) malloc(sizeof(int)*n);
会创build相同数量的内存,但在堆上。 在这种情况下,内存中的内容与范围没有关系,只有内存的引用受范围的限制。 这里是按值传递的地方,通过引用传入的地方。按值传递,就像你可能知道的那样,意味着当某个东西传入或者从函数返回时,传递的“东西”就是评估variables的结果。 换一种说法,
int n = 4; printf("%d", n);
将打印数字4,因为构造n
评估为4(抱歉,如果这是基本的,我只想涵盖所有的基础)。 这4个对程序的内存空间没有任何影响或关系,只是一个字面意思,所以一旦你离开这个4的上下文的范围,就会失去它。 通过参考怎么样? 通过引用传递在函数的上下文中是没有区别的; 您只需评估通过的构造。 唯一的区别是在评估通过的“东西”之后,将评估结果作为内存地址。 我曾经有一个愤世嫉俗的CS教练,他喜欢说,没有任何事情可以通过参考,只是一种传递聪明的价值观的方式。 真的,他是对的。 所以现在我们考虑一个函数的范围。 假装你可以有一个数组返回types:
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:一切都只是一个指针。 而且他也是对的。 尽pipeJava中的所有内容实际上都是按值传递的,但几乎所有这些值都是内存地址。 所以在Java中,这种语言可以让你返回一个数组或string,但是通过将它转换为带有指针的版本来实现。 它也为你pipe理你的记忆。 而自动内存pipe理虽然有用,但效率不高。
这将我们带到C ++。 C ++被发明的原因是因为Bjarne Stroustrup在博士研究期间一直在尝试Simula(基本上是原始的OOPL),并且认为它在概念上是太棒了,但是他注意到它performance得相当可怕。 于是他开始研究所谓的C with Classes,它被重命名为C ++。 在这样做的过程中,他的目标是编写一个编程语言,使Simula的一些最好的function,但仍然强大和快速。 他之所以select扩展C,是因为它已经有了传奇性的performance,而且他select不像其他OOPL那样大规模实施自动内存pipe理或垃圾收集。 从一个模板类返回一个数组是可行的,因为,你正在使用一个类。 但是如果你想返回一个C数组,你必须用C方法来完成。 换句话说,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
是一个类,但是当你说一个string,你可能意味着一个文字。 你可以从函数中安全地返回一个字面值,但实际上你可以静态地创build任何数组并从函数中返回。 如果它是一个const(只读)数组,这是string文字的情况下,这将是线程安全的。
你返回的数组虽然会降级到一个指针,所以你不能从它的返回中计算出它的大小。
返回一个数组,如果可能的话,首先必须是固定的长度,因为编译器需要创build调用栈,然后存在数组不是l值的问题,所以在调用函数中接收它将不得不使用初始化一个新的variables,这是不切实际的。 因为同样的原因,返回一个也可能是不切实际的,尽pipe它们可能使用了特殊的符号来表示返回值。
请记住,在C的早期,所有的variables都必须在函数的顶部声明,并且不能在第一次使用时声明。 因此当时是不可行的。
他们给出了将数组放入一个结构的解决方法,这就是它现在必须保持在C ++中,因为它使用相同的调用约定。
注意:在像Java这样的语言中,数组是一个类。 你用新的创build一个。 你可以重新分配它们(它们是l值)。
C中的数组(和C ++中的向后兼容)具有与其他types不同的特殊语义。 特别是,对于其余types,C只具有按值传递的语义,在数组的情况下,按值传递语法的效果以奇怪的方式模拟传递引用:
在函数签名中, types为T的N个元素的数组types的参数被转换为指向T的指针 。 在函数调用中,将一个数组作为parameter passing给一个函数会使数组衰减到一个指向第一个元素的指针 ,并将该指针复制到函数中。
由于这种特殊的数组处理方式 – 它们不能被值传递,所以它们也不能被返回值。 在C中你可以返回一个指针,而在C ++中你也可以返回一个引用,但是这个数组本身不能被分配到栈中。
如果你想到这个问题,这与你在问题中使用的语言没有什么不同,因为数组是dynamic分配的,你只是返回一个指针/引用。
另一方面,C ++语言为这个特定的问题提供了不同的解决scheme,比如在即将到来的标准中使用当前标准(内容是dynamic分配的)或std::array
中的std::vector
(内容可以在堆栈中分配,但是可能会有更大的成本,因为在复制不能被编译器消除的情况下,每个元素都必须被复制)。 实际上,通过使用像boost::array
这样的现成的库,你可以在当前的标准中使用相同types的方法。
其他人说,在C ++中,一个使用vector <>而不是从Cinheritance的数组。
那么为什么C ++不允许返回C数组呢? 因为C不。
为什么C不? 因为C是从B演变而来的,一种无types的语言,在这种语言中返回一个数组根本没有意义。 当向B中添加types时,使返回一个数组成为可能是有意义的,但是为了保持一些B成语是有效的,并且使得程序从B到C的转换变得容易,没有这样做。从那以后,使C数组更容易被使用(甚至更多,甚至没有考虑到),因为它会破坏太多的代码。
“你不能从函数中返回数组,因为这个数组是在函数内部声明的,所以它的位置就是堆栈帧,但是当函数退出的时候,栈帧被擦除,函数必须从栈帧中返回值返回位置,这对arrays来说是不可能的。“
从这里的讨论:
http://forum.codecall.net/cc/32457-function-return-array-c.html
你可以返回一个指向数组的指针。 稍后请注意释放内存。
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。
“为什么C ++不支持类似的东西”:因为这没有任何意义。 在像JAVA或PHP这样基于参考的语言中,内存pipe理是基于垃圾回收的。 内存中没有引用的部分(程序中没有variables指向它)被自动释放。 在这种情况下,你可以分配内存,并通过引用周围carefreely。
C ++代码将被转换为机器代码,并且没有定义GC。 所以在C和C ++中,存储块的所有权意识很强。 你必须知道你所使用的指针是否可以在任何时候释放(实际上你在使用之后就可以释放它),或者你有一个指向内存共享部分的指针,这是一个绝对的空闲空间。
在这种环境下,每次从一个函数传入或传出数组时,都不会获得无穷无尽的副本。 以类似C语言来pipe理数据的数组是非常复杂的任务。 没有一种万能的解决scheme,你需要知道什么时候释放内存。
一个函数返回的数组是否永远是一个副本(你的自由),或者你必须复制它们? 你会赢得一个数组指针指向数组吗?
返回一个std::vector<>
而不是一个数组。 一般来说,C ++不能很好的工作,通常应该避免。
此外, string
数据types不只是一个字符数组,尽pipe“引用的string”是。 string
pipe理着一个字符数组,你可以通过.c_str()
来访问它,但是除此之外还有更多的string
。
看看这里。 真的有帮助。
- 如何从函数返回数组?
- C ++从函数返回multidimensional array
- 从C ++中的函数返回2d数组