为什么C ++不支持返回数组的函数?

时间:2011-03-01 16:39:00

标签: c++ arrays function

有些语言可以让你只声明一个像普通函数一样返回数组的函数,比如Java:

public String[] funcarray() {
   String[] test = new String[]{"hi", "hello"};
   return test;
}

为什么C ++不支持int[] funcarray(){}之类的东西? 你可以返回一个数组,但是制作这样一个函数真的很麻烦。而且,我听说字符串只是char的数组。所以如果你能用C ++返回一个字符串,为什么不用数组?

10 个答案:

答案 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 ++ 可以支持甚至返回动态大小的数组,但它们不支持。我敢肯定有合理的理由,但是可以。

所有您需要做的就是在堆栈上分配动态数组,返回它的地址和大小,并确保调用者将堆栈指针增加到返回数组的末尾。可能需要进行一些堆栈框架固定,但绝不是不可能的。