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

Loc*_*ead 45 c++ arrays function

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

public String[] funcarray() {
   String[] test = new String[]{"hi", "hello"};
   return test;
}
Run Code Online (Sandbox Code Playgroud)

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

Dou*_*hen 61

我猜想要简明扼要,这只是一个设计决定.更具体地说,如果你真的想知道原因,你需要从头开始工作.

让我们先考虑一下C.在C语言中,"按引用传递"和"按值传递"之间存在明显的区别.为了轻视它,C中数组的名称实际上只是一个指针.对于所有意图和目的,差异(通常)归结为分配.代码

int array[n];
Run Code Online (Sandbox Code Playgroud)

将在堆栈上创建4*n字节的内存(在32位系统上),与任何代码块进行声明的范围相关.反过来,

int* array = (int*) malloc(sizeof(int)*n);
Run Code Online (Sandbox Code Playgroud)

会创建相同数量的内存,但在堆上.在这种情况下,该内存中的内容与范围无关,只有内存的引用受范围的限制.这里是按值传递和按引用传递的地方.正如您可能知道的那样,传递值意味着当某个东西传递给函数或从函数返回时,传递的"东西"是评估变量的结果.换一种说法,

int n = 4;
printf("%d", n);
Run Code Online (Sandbox Code Playgroud)

将打印数字4,因为构造n评估为4(对不起,如果这是基本的,我只想覆盖所有基础).这4与你的程序的内存空间完全没有关系或关系,它只是一个文字,所以一旦你离开了4具有上下文的范围,你就会失去它.通过参考传递怎么样?通过引用传递在函数的上下文中没有区别; 您只需评估传递的构造.唯一的区别是在评估传递的"事物"之后,您将评估结果用作内存地址.我曾经有一个特别愤世嫉俗的CS导师,他喜欢说没有通过引用传递这样的东西,只是一种传递聪明价值的方法.真的,他是对的.所以现在我们考虑功能方面的范围.假装你可以有一个数组返回类型:

int[] foo(args){
    result[n];
    // Some code
    return result;
}
Run Code Online (Sandbox Code Playgroud)

这里的问题是结果计算到数组的第0个元素的地址.但是当您尝试从此函数外部(通过返回值)访问此内存时,您遇到了问题,因为您正在尝试访问不在您正在使用的作用域内的内存(函数调用的堆栈).因此,我们解决这个问题的方法是使用标准的"传递参考"jiggery-pokery:

int* foo(args){
    int* result = (int*) malloc(sizeof(int)*n));
    // Some code
    return result;
}
Run Code Online (Sandbox Code Playgroud)

我们仍然得到一个指向数组第0个元素的内存地址,但现在我们可以访问该内存了.

我的观点是什么?在Java中,通常断言"一切都是通过价值传递".这是真的.上面同样的愤世嫉俗的教师也对Java和OOP有这样的说法:一切都只是一个指针.他也是对的.虽然Java中的所有内容实际上都是按值传递的,但几乎所有这些值实际上都是内存地址.所以在Java中,该语言确实允许您返回数组或字符串,但它通过将其转换为带有指针的版本来实现.它还为您管理您的记忆.而自动内存管理虽然有用但效率不高.

这将我们带到了C++.C++被发明的全部原因是因为Bjarne Stroustrup在他的博士工作期间一直在试验Simula(基本上是原始的OOPL),并且认为它在概念上很棒,但是他注意到它的表现非常糟糕.所以他开始研究C with Classes,它被重命名为C++.在这样做的过程中,他的目标是制作一种编程语言,该语言采用了Simula的一些最佳功能,但仍然保持强大和快速.由于其已经具有传奇色彩的表现,他选择扩展C,并且一个权衡是他选择不像其他OOPL那样大规模地实现自动内存管理或垃圾收集.从其中一个模板类返回一个数组是有效的,因为你正在使用一个类.但是如果你想返回一个C数组,你必须以C方式进行.换句话说,C++确实支持以与Java相同的方式返回数组; 它只是没有为你做所有的工作.因为一个丹麦人认为它太慢了.

  • 不再......**数组和指针是不同的野兽**这种类型的答案,即使用*来轻视它*限定符只会增加混乱. (4认同)
  • 我也从未说过数组是一个指针.我说数组的名称是一个指针.虽然在语义上非常错误,但这只是一种简短且非技术性的说法,除非在非常特殊的情况下,类型T的数组的NAME将衰减为指向第一个元素的T型指针,尽管不言而喻,数组的名称是不可修改的左值.但遗憾的是抱歉.我理解你的担忧. (2认同)
  • 这应该被提名为某种令人敬畏的答案奖。我刚刚学到了一大堆东西,因为它重新整理了我一直知道并认为理所当然的东西。 (2认同)

Cas*_*Cow 31

C++确实支持它 - 好吧:

vector< string> func()
{
   vector<string> res;
   res.push_back( "hello" );
   res.push_back( "world" );
   return res;
}
Run Code Online (Sandbox Code Playgroud)

甚至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;
}
Run Code Online (Sandbox Code Playgroud)

A std::string是一个类但是当你说一个字符串时,你可能意味着一个文字.您可以安全地从函数返回文字,但实际上您可以静态创建任何数组并从函数返回它.如果它是一个const(只读)数组,这就是字符串文字的情况,这将是线程安全的.

您返回的数组会降级为指针,因此您无法仅从返回时计算出其大小.

如果可能的话,返回一个数组,首先必须是固定长度,因为编译器需要创建调用堆栈,然后出现数组不是l值的问题,因此在调用函数中接收它必须使用带有初始化的新变量,这是不切实际的.由于同样的原因,返回一个也可能是不切实际的,尽管它们可能对返回值使用了特殊的符号.

请记住,在C的早期,所有变量都必须在函数的顶部声明,并且您不能在第一次使用时声明.因此,当时这是不可行的.

他们给出了将数组放入结构的解决方法,这就是它现在必须保留在C++中的原因,因为它使用相同的调用约定.

注意:在Java等语言中,数组是一个类.你创建一个新的.你可以重新分配它们(它们是l值).

  • 如果数组的大小在编译时是固定的,你可以使用时间std :: array <X,N>(或std :: tr1 :: array <X,N>或boost :: array <X,N> ). (3认同)

Dav*_*eas 26

C中的数组(以及C++中的向后兼容性)具有与其他类型不同的特殊语义.特别是,对于其余类型,C只具有按值传递的语义,在数组的情况下,传值语法的效果以奇怪的方式模拟传递引用:

在函数签名中,类型为T的N个元素的类型数组的参数将转换为指向T的指针.在函数调用中,将数组作为参数传递给函数会将数组衰减指向第一个元素指针,并将该指针复制到函数中.

由于对阵列的这种特殊处理 - 它们不能通过值传递 - 它们也不能通过值返回.在C中你可以返回一个指针,在C++中你也可以返回一个引用,但是数组本身不能在栈中分配.

如果你想到它,这与你在问题中使用的语言没有什么不同,因为数组是动态分配的,你只返回一个指针/引用.

另一方面,C++语言为该特定问题提供了不同的解决方案,例如std::vector在当前标准中使用(内容是动态分配的)或std::array在即将推出的标准中(内容可以在堆栈中分配,但可能会有更高的成本) ,因为在编译器无法省略副本的情况下,必须复制每个元素.实际上,您可以使用与现有标准相同类型的方法,使用现成的库boost::array.


Orb*_*bit 8

"你不能从函数返回数组,因为该数组将在函数内声明,然后它的位置就是堆栈帧.但是,当函数退出时,堆栈帧被擦除.函数必须从堆栈帧复制返回值以返回位置,这对阵列来说是不可能的."

从这里的讨论:

http://forum.codecall.net/cc/32457-function-return-array-c.html

  • 我没有看到引用的问题,链接引用. (7认同)
  • 我不能同意这个答案.对于大多数其他类型,您可以按值返回,并且返回的对象位于函数内部没有问题:复制(或者如果编译器设法这样做则省略).这是一种常见的行为,并且不能用数组完成同样的事实更多的是C语言中的设计决策 - 在C++中已经完成.事实上,如果将数组包含在结构中,那就是将要发生的事情:struct(包括内部数组)将被复制到return语句中. (7认同)

APr*_*mer 7

其他人说在C++中,一个使用vector <>而不是从C继承的数组.

那么为什么C++不允许返回C数组呢?因为C没有.

为什么C没有?因为C是从B演化而来的,这是一种无类型的语言,其中返回数组根本没有意义.当向B添加类型时,使得返回数组成为可能是有意义的但是为了使某些B成语保持有效并且使程序从B到C的转换变得容易而没有这样做.从那时起,可能性使C数组更可用,因为它总是被拒绝(甚至更多,甚至没有考虑),因为它会破坏太多的现有代码.