在函数中返回数组


212

我有一个int arr[5]传递给函数的数组fillarr(int arr[])

int fillarr(int arr[])
{
    for(...);
    return arr;
}
  1. 如何返回该数组?
  2. 我将如何使用它,说我返回了一个指针,我将如何访问它?

46
严格来说,在这种情况下,您不需要返回数组,因为该数组是通过引用传递的,因此对“ arr”内部元素的任何更改都可以在函数外部看到。
BuggerMe 2010年

12
返回数组对于链接函数很方便。
seand 2010年

5
只要您没有犯错就可以在堆栈上创建数组并返回指向它的指针。
2010年

2
@ismail:它不能返回一个新数组,除非该数组是动态分配的。如果是这种情况,请使用std::vector
GManNickG

4
@BuggerMe:数组不是通过引用传递的(除非您使用更有趣的语法来请求),在代码中,数组会衰减为指向第一个元素的指针,然后传递给函数。将5在函数签名是由编译器丢弃。
大卫·罗德里格斯(DavidRodríguez)-dribeas 2010年

Answers:


204

在这种情况下,arr通过隐式转换,实际上也可以将数组变量视为指向内存中数组块开头的指针。您使用的语法如下:

int fillarr(int arr[])

只是一种语法糖。您真的可以用它替换它,它仍然可以工作:

int fillarr(int* arr)

因此,从同样的意义上说,您要从函数中返回的内容实际上是指向数组中第一个元素的指针:

int* fillarr(int arr[])

而且您仍然可以像使用普通数组一样使用它:

int main()
{
  int y[10];
  int *a = fillarr(y);
  cout << a[0] << endl;
}

45
澄清一下,“经典C ++语句”是错误的。数组不是指针。
GManNickG

25
记住a [i] == *(a + i)规则
肖恩(Sean

8
@布伦特·纳什(Brent Nash),不。一个数组就是一个数组。指向数组开头的指针是一个指针。碰巧编译器有一些语法糖可以在某些情况下为您进行翻译。 array并且&array在很多情况下是可以互换的。
卡尔·诺鲁姆

20
@Brent:否。数组是它自己的类型,它不是一种特殊的指针。ain 的类型int a[10]int[10]。您可以说的是数组“衰减”到指向第一个元素的指针中。(这是隐式的数组到指针的转换。)然后,您的答案将遵循我的观点。如果您编辑答案以区分数组,数组到指针转换和指针,我将删除答案,因为它们将具有相同的核心信息,并且您是第一位的。
GManNickG

8
@seand记住a [i] == *(a + sizeof(a)* i)规则
Amir

114

C ++函数无法按值返回C样式数组。最接近的事情是返回一个指针。此外,参数列表中的数组类型仅转换为指针。

int *fillarr( int arr[] ) { // arr "decays" to type int *
    return arr;
}

您可以通过对参数和return使用数组引用来改进它,以防止衰减:

int ( &fillarr( int (&arr)[5] ) )[5] { // no decay; argument must be size 5
    return arr;
}

使用Boost或C ++ 11时,按引用传递仅是可选的,语法不太费力:

array< int, 5 > &fillarr( array< int, 5 > &arr ) {
    return arr; // "array" being boost::array or std::array
}

array模板简单地生成一个struct包含C风格的数组,这样你就可以申请面向对象的语义,但仍保留了磁盘阵列的原始简单。


4
+1给出了如何通过引用传递数组的示例。但是您错了,因为您不能通过引用返回数组。实现它的最简单语法是使用typedef:typedef int array[5]; array& foo();但是,如果您愿意编写this:甚至不需要typedef int (&foo())[5] { static int a[5] = {}; return a; },问题中的示例将是:int (&foo( int (&a)[5] ))[5] { return a; }。很简单,不是吗?
大卫·罗德里格斯(DavidRodríguez)-dribeas

@David:谢谢,我收到来自Comeau消息的错误印象,error: function returning array is not allowed如果您使用non-typedef语法省略外部括号,则会出现该消息。幸运的是,今天,我回顾了另一个问题的左右定律,并设法构造出正确的东西……在看到您说是可能的……之后,看到了您给出的代码:vP。
Potatoswatter 2010年

1
chubsdad的答案使用标准中的正确引号:您不能返回数组,但是可以返回引用或指向数组的指针。数组是不可复制的(作为一种类型),因此它们不能返回(这意味着一个副本),并且当存在该语法时,编译器会将参数转换为指针。
大卫·罗德里格斯(DavidRodríguez)-dribeas 2010年

3
@David:是的。该页面变得异常长。从来没有这么多人自愿编写如此多的琐碎函数来在一个地方返回一个数组。
Potatoswatter

@Potatoswatter我是cpp的新手,能否详细解释第二个代码段?为了理解,我无法将其分为几部分。
毕马威(KPMG)

23

在C ++ 11中,您可以返回std::array

#include <array>
using namespace std;

array<int, 5> fillarr(int arr[])
{
    array<int, 5> arr2;
    for(int i=0; i<5; ++i) {
        arr2[i]=arr[i]*2;
    }
    return arr2;
}

2
引用OP:(...) you can consider the array returned arr2, totally another array (...)
cubuspl42,2014年

22

$ 8.3.5 / 8个州-

“函数不得具有类型数组或函数的返回类型,尽管它们可能具有类型指针的返回类型或对此类事物的引用。不应包含函数数组,尽管可以存在指向函数的指针数组。”

int (&fn1(int (&arr)[5]))[5]{     // declare fn1 as returning refernce to array
   return arr;
}

int *fn2(int arr[]){              // declare fn2 as returning pointer to array
   return arr;
}


int main(){
   int buf[5];
   fn1(buf);
   fn2(buf);
}

7
您的第二个函数返回指向而int不是数组的指针。
GManNickG

再次,为什么在函数内部更新实际数组时返回类型?这是最佳做法吗?

14

答案可能取决于您打算如何使用该功能。对于最简单的答案,让我们确定您真正想要的是向量而不是数组。向量之所以不错,是因为它看起来像无聊的普通值,您可以将它们存储在常规指针中。我们将查看其他选项以及之后为什么要使用它们:

std::vector<int> fillarr( std::vector<int> arr ) {
    // do something
    return arr;
}

这将完全符合您的期望。这样做的好处是std::vector要确保一切都被干净地处理。不利的一面是,如果您的阵列很大,那么它将复制大量数据。实际上,它会将数组的每个元素复制两次。首先,它会复制向量,以便函数可以将其用作参数。然后它将再次复制以将其返回给调用方。如果您可以自己管理向量,则可以更轻松地完成操作。(如果调用方需要将其存储在某种变量中以进行更多计算,则可能会第三次复制它)

看来您真正要执行的操作只是填充集合。如果您没有特定的原因要返回集合的新实例,请不要这样做。我们可以这样做

void fillarr(std::vector<int> &  arr) {
    // modify arr
    // don't return anything
}

这样,您可以获得对传递给函数的数组的引用,而不是其私有副本。您对参数所做的任何更改都会被调用者看到。您可以根据需要返回对它的引用,但这并不是一个好主意,因为它暗示您所获得的东西与您通过的东西有所不同。

如果确实需要集合的新实例,但又想避免将其放到堆栈中(以及随之而来的所有复制操作),则需要为该实例的处理方式创建某种形式的合同。最简单的方法是使用智能指针,只要有人保持对它的引用,该指针就一直保持引用的实例不变。如果超出范围,它将完全消失。看起来像这样。

std::auto_ptr<std::vector<int> > fillarr( const std::vector<int> & arr) {
    std::auto_ptr<std::vector<int> > myArr(new std::vector<int>);
    // do stuff with arr and *myArr
    return myArr;
}

在大多数情况下,using的*myArr工作原理与使用普通香草矢量的效果相同。此示例还通过添加const关键字来修改参数列表。现在您获得了一个引用,但没有复制它,但是您无法对其进行修改,因此调用者知道该引用与该函数获取之前的引用相同。

所有这些都是膨胀的,但是惯用的c ++很少与整个集合一起使用。通常,将在这些集合上使用迭代器。看起来会更像这样

template <class Iterator>
Iterator fillarr(Iterator arrStart, Iterator arrEnd) {
    Iterator arrIter = arrStart;
    for(;arrIter <= arrEnd; arrIter++)
       ;// do something
    return arrStart;
}

如果您不习惯这种样式,使用它看起来会有些奇怪。

vector<int> arr;
vector<int>::iterator foo = fillarr(arr.begin(), arr.end());

foo现在“指向”修饰符的开头arr

真正令人高兴的是,它在向量上以及在普通C数组和许多其他类型的集合上都可以很好地工作

int arr[100];
int *foo = fillarr(arr, arr+100);

现在看起来很像这个问题中其他地方给出的普通指针示例。


语法是错误的,&符号必须类型后会出现:void fillarr(std::vector<int> & arr)
大卫·罗德里格斯- dribeas

9

这个:

int fillarr(int arr[])

实际上被视为与以下内容相同:

int fillarr(int *arr)

现在,如果您真的想返回一个数组,可以将该行更改为

int * fillarr(int arr[]){
    // do something to arr
    return arr;
}

它不是真的返回数组。您将返回一个指向数组地址开头的指针。

但是请记住,当您传递数组时,您仅传递了一个指针。因此,当您修改数组数据时,实际上是在修改指针所指向的数据。因此,在传递数组之前,必须意识到外部已经具有修改后的结果。

例如

int fillarr(int arr[]){
   array[0] = 10;
   array[1] = 5;
}

int main(int argc, char* argv[]){
   int arr[] = { 1,2,3,4,5 };

   // arr[0] == 1
   // arr[1] == 2 etc
   int result = fillarr(arr);
   // arr[0] == 10
   // arr[1] == 5    
   return 0;
}

我建议您可能要考虑像这样在您的fillarr函数中添加一个长度。

int * fillarr(int arr[], int length)

这样,无论长度是多少,都可以使用length来填充数组的长度。

正确使用它。做这样的事情:

int * fillarr(int arr[], int length){
   for (int i = 0; i < length; ++i){
      // arr[i] = ? // do what you want to do here
   }
   return arr;
}

// then where you want to use it.
int arr[5];
int *arr2;

arr2 = fillarr(arr, 5);

// at this point, arr & arr2 are basically the same, just slightly
// different types.  You can cast arr to a (char*) and it'll be the same.

如果您只想将数组设置为一些默认值,请考虑使用内置的memset函数。

类似的东西:memset((int *)&arr,5,sizeof(int));

虽然我在这个话题上。您说您正在使用C ++。看看使用stl向量。您的代码可能更健壮。

有很多教程。这是一个让您了解如何使用它们的想法。 http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html


使用std::copy结束memset,它更安全,更轻松。(而且仅仅是速度如果不是更快)
GManNickG

5

要从函数返回数组,让我们在结构中定义该数组;所以看起来像这样

struct Marks{
   int list[5];
}

现在让我们创建类型结构的变量。

typedef struct Marks marks;
marks marks_list;

我们可以通过以下方式将数组传递给函数并为其赋值:

void setMarks(int marks_array[]){
   for(int i=0;i<sizeof(marks_array)/sizeof(int);i++)
       marks_list.list[i]=marks_array[i];
}

我们也可以返回数组。要返回数组,函数的返回类型应为结构类型,即标记。这是因为实际上我们正在传递包含数组的结构。因此,最终代码可能如下所示。

marks getMarks(){
 return marks_list;
}

5

这是一个相当老的问题,但是我要投入2美分,因为有很多答案,但是没有一个以简洁明了的方式展示所有可能的方法(不确定简明之处,因为这有点位(TL; DR😉)。

我假设OP希望返回不复制而传递的数组,这是将其直接传递给调用方并传递给另一个函数以使代码更漂亮的某种方式。

然而,使用一个数组这样是让它衰变到一个指针和具有编译器把它一个数组。如果您传入类似的数组,这可能会导致细微的错误,该函数期望它包含5个元素,但是调用者实际上传入了其他一些数字。

有几种方法可以更好地处理此问题。传递一个std::vectorstd::array(不确定是否std::array在2010年问这个问题时)。然后,您可以将对象作为参考传递,而无需任何复制/移动对象。

std::array<int, 5>& fillarr(std::array<int, 5>& arr)
{
    // (before c++11)
    for(auto it = arr.begin(); it != arr.end(); ++it)
    { /* do stuff */ }

    // Note the following are for c++11 and higher.  They will work for all
    // the other examples below except for the stuff after the Edit.

    // (c++11 and up)
    for(auto it = std::begin(arr); it != std::end(arr); ++it)
    { /* do stuff */ }

    // range for loop (c++11 and up)
    for(auto& element : arr)
    { /* do stuff */ }

    return arr;
}

std::vector<int>& fillarr(std::vector<int>& arr)
{
    for(auto it = arr.begin(); it != arr.end(); ++it)
    { /* do stuff */ }
    return arr;
}

但是,如果您坚持使用C数组,请使用一个模板,该模板将保留数组中有多少项的信息。

template <size_t N>
int(&fillarr(int(&arr)[N]))[N]
{
    // N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
    for(int* it = arr; it != arr + N; ++it)
    { /* do stuff */ }
    return arr;
}

除了那看起来很难看,而且很难读。我现在使用一些东西来帮助解决2010年没有出现的问题,我也将其用于函数指针:

template <typename T>
using type_t = T;

template <size_t N>
type_t<int(&)[N]> fillarr(type_t<int(&)[N]> arr)
{
    // N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
    for(int* it = arr; it != arr + N; ++it)
    { /* do stuff */ }
    return arr;
}

这使其中一个会希望它是,使得这种类型远远更具可读性。当然,如果您只使用5个元素,那么使用模板是多余的,因此您当然可以对其进行硬编码:

type_t<int(&)[5]> fillarr(type_t<int(&)[5]> arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

就像我说的那样,type_t<>在提出这个问题时,我的trick俩是行不通的。那时您可能希望的最好的办法是在结构中使用类型:

template<typename T>
struct type
{
  typedef T type;
};

typename type<int(&)[5]>::type fillarr(typename type<int(&)[5]>::type arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

虽然看起来可能很丑陋,但至少仍然更具可读性,尽管typename那时取决于编译器可能是可选的,从而导致:

type<int(&)[5]>::type fillarr(type<int(&)[5]>::type arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

然后,您当然可以指定一个特定的类型,而不是使用我的帮助器。

typedef int(&array5)[5];

array5 fillarr(array5 arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

当时,自由的功能std::begin()std::end()不存在,但可能已经很容易实现。这将允许以一种更安全的方式遍历数组,因为它们在C数组上有意义,但在指针上却没有意义。

至于访问数组,您既可以将其传递给具有相同参数类型的另一个函数,也可以为其别名(这在您已经拥有该范围的原始对象中就没有多大意义了)。访问数组引用就像访问原始数组一样。

void other_function(type_t<int(&)[5]> x) { /* do something else */ }

void fn()
{
    int array[5];
    other_function(fillarr(array));
}

要么

void fn()
{
    int array[5];
    auto& array2 = fillarr(array); // alias. But why bother.
    int forth_entry = array[4];
    int forth_entry2 = array2[4]; // same value as forth_entry
}

总而言之,如果要迭代数组,最好不要让数组衰减为指针。这是一个坏主意,因为它使编译器无法保护您免于脚步,并使您的代码更难阅读。除非您有充分的理由不这样做,否则请始终尝试通过尽可能长时间保持类型来帮助编译器为您提供帮助。

编辑

哦,为了完整起见,您可以允许它降级为一个指针,但这会使数组与其持有的元素数量脱钩。这在C / C ++中已完成很多工作,通常可以通过传递数组中的元素数来缓解。但是,如果您犯了错误并将错误的值传递给元素数量,则编译器将无法为您提供帮助。

// separate size value
int* fillarr(int* arr, size_t size)
{
    for(int* it = arr; it != arr + size; ++it)
    { /* do stuff */ }
    return arr;
}

您可以传递结束指针,而不是传递大小,该指针将指向数组末尾的1。这很有用,因为它可以使它更接近于std算法,该算法需要使用begin和end指针,但是现在返回的只是您必须记住的内容。

// separate end pointer
int* fillarr(int* arr, int* end)
{
    for(int* it = arr; it != end; ++it)
    { /* do stuff */ }
    return arr;
}

另外,您可以记录该函数仅包含5个元素,并希望函数的用户不要做任何愚蠢的事情。

// I document that this function will ONLY take 5 elements and 
// return the same array of 5 elements.  If you pass in anything
// else, may nazal demons exit thine nose!
int* fillarr(int* arr)
{
    for(int* it = arr; it != arr + 5; ++it)
    { /* do stuff */ }
    return arr;
}

请注意,返回值已丢失其原始类型,并降级为指针。因此,您现在可以自己确保不会超出阵列。

您可以传递一个std::pair<int*, int*>,可以将其用于开始和结束,并将其传递出去,但是随后它实际上不再像数组一样。

std::pair<int*, int*> fillarr(std::pair<int*, int*> arr)
{
    for(int* it = arr.first; it != arr.second; ++it)
    { /* do stuff */ }
    return arr; // if you change arr, then return the original arr value.
}

void fn()
{
    int array[5];
    auto array2 = fillarr(std::make_pair(&array[0], &array[5]));

    // Can be done, but you have the original array in scope, so why bother.
    int fourth_element = array2.first[4];
}

要么

void other_function(std::pair<int*, int*> array)
{
    // Can be done, but you have the original array in scope, so why bother.
    int fourth_element = array2.first[4];
}

void fn()
{
    int array[5];
    other_function(fillarr(std::make_pair(&array[0], &array[5])));
}

有趣的是,这与std::initializer_list工作方式(c ++ 11)非常相似,但是它们在这种情况下不起作用。


3

最简单的方法是按引用返回它,即使您不写'&'符号,它也会按引用自动返回

     void fillarr(int arr[5])
  {
       for(...);

  }

2
int *fillarr(int arr[])

您仍然可以像这样使用结果

int *returned_array = fillarr(some_other_array);
if(returned_array[0] == 3)
    do_important_cool_stuff();

我认为'int [] fillarr ...'不合法。由于数组指针等效,将使用“ int * fillarr”。
seand 2010年

1

如上所述,路径是正确的。但是我认为,如果我们仅返回函数的局部数组变量,则有时它会返回垃圾值作为其元素。

为了避免我不得不动态创建数组并继续。是这样的。

int* func()
{
  int* Arr = new int[100];
  return Arr;
}

int main()
{
  int* ArrResult = func();
  cout << ArrResult[0] << " " << ArrResult[1] << endl;
  return 0;
} 





0

资料来源:https : //www.tutorialspoint.com/cplusplus/cpp_return_arrays_from_functions.htm

C ++不允许将整个数组作为函数的参数返回。但是,您可以通过指定不带索引的数组名称来返回指向数组的指针。

  1. 如果要从函数返回一维数组,则必须声明一个返回指针的函数,如以下示例所示:
int * myFunction()    {
   .
   .
   .
}
  1. C ++不主张将局部变量的地址返回到函数外部,因此您必须将局部变量定义为静态变量。

将这些规则应用于当前问题,我们可以编写如下程序:

# include <iostream>

using namespace std;

int * fillarr( );


int main ()
{

   int *p;

   p = fillarr();

   for ( int i = 0; i < 5; i++ )
       cout << "p[" << i << "] : "<< *(p + i) << endl;

    return 0;
}


int * fillarr( )
{
    static int  arr[5];

    for (int i = 0; i < 5; ++i)
        arr[i] = i;

    return arr;
 }

输出将是:

p[0]=0
p[1]=1
p[2]=2
p[3]=3
p[4]=4

0

以及什么:

int (*func())
{
    int *f = new int[10] {1,2,3};

    return f;
}

int fa[10] = { 0 };
auto func2() -> int (*) [10]
{
    return &fa;
}

0

实际上,当您在函数内部传递数组时,指向原始数组的指针会在function参数中传递,因此对该函数内部的数组所做的更改实际上是在原始数组上进行的。

#include <iostream>

using namespace std;

int* func(int ar[])
{
    for(int i=0;i<100;i++) 
        ar[i]=i;
    int *ptr=ar;
    return ptr;
}


int main() {
    int *p;
    int y[100]={0};    
    p=func(y);

    for(int i=0;i<100;i++) 
        cout<<i<<" : "<<y[i]<<'\n';
}

运行它,您将看到更改


1
请使用正确的英语措词(您将代替u),并省略空词组,例如“ buddy”。
hellow

另外:“然后实际上将其作为参考传递”是错误的。变量y本身是作为其自身的副本传递的,但是由于它是指针,因此您将直接在数组上进行操作。请修改您的答案。
hellow

stackoverflow.com/questions/5573310/…TL ; DR “因此,两种形式是相同的。”
hellow

是的,从技术上讲,它是一个数组,您是对的,但是复制的是指向数组的指针,而不是数组本身。
hellow

0

这是解决此类问题的完整示例

#include <bits/stdc++.h>
using namespace std;
int* solve(int brr[],int n)
{
sort(brr,brr+n);
return brr;
}

int main()
{
int n;
cin>>n;
int arr[n];
for(int i=0;i<n;i++)
{
    cin>>arr[i];
}
int *a=solve(arr,n);
for(int i=0;i<n;i++)
{
    cout<<a[i]<<endl;
}

return 0;
}

-2

只需将type []定义为返回值即可,例如:

        private string[] functionReturnValueArray(string one, string two)
    {

        string[] x = {one, two};


        x[0] = "a";
        x[1] = "b";

        return x;
    }

。。。函数调用:

string[] y;
y = functionReturnValueArray(stringOne, stringTwo)

5
这不是C ++
Adrian
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.