在函数中返回数组

Posted

技术标签:

【中文标题】在函数中返回数组【英文标题】:Return array in a function 【发布时间】:2011-03-29 06:09:02 【问题描述】:

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

int fillarr(int arr[])

    for(...);
    return arr;

    如何返回该数组? 我将如何使用它,比如我返回了一个指针,我将如何访问它?

【问题讨论】:

严格来说,在这种情况下,您不需要返回数组,因为数组是通过引用传递的,因此对“arr”内部元素的任何更改都会在函数外部看到。 返回数组方便函数链式。 只要你没有在堆栈上创建一个数组并返回一个指向它的指针的错误。 @BuggerMe:数组不是通过引用传递(除非您使用更有趣的语法请求它),在代码中,数组 decays指向第一个元素的指针,并将其传递给函数。函数签名中的5 被编译器丢弃。 @BuggerMe:不是,不是。我很准确,因为我已经习惯了人们误解 C++ 中数组的 pass-by-value 语法的语义。通过引用传递数组是:void foo( int (&array)[5] );(通过引用传递 5 个整数的数组)。当您通过引用传递时,您在函数中得到的是对实际类型的 reference。另一方面,void foo( int array[5] ) 在函数定义期间由编译器翻译成void foo(int*)。调用 foo( myarray ) 会产生数组的 decay 指向第一个元素的指针。 【参考方案1】:

为什么不将数组作为参数“返回”?

fillarr(int source[], size_t dimSource, int dest[], size_t dimDest)


    if (dimSource <= dimDest)
    
        for (size_t i = 0; i < dimSource; i++)
           
            //some stuff...
        
    
    else 
    
        //some stuff..
    

或者..以更简单的方式(但您必须知道尺寸...):

fillarr(int source[], int dest[])

    //...

【讨论】:

【参考方案2】:

我使用了静态数组,因此在返回数组时它不应该在返回局部变量的地址时抛出错误... 因此,现在您可以通过将其设为静态来从函数中发送任何本地创建的变量......因为它作为全局变量工作......

#include<iostream>
using namespace std;

char *func(int n)

   // char a[26]; /*if we use this then an error will occur because you are 
                        //  returning address of a local variable*/
    static char a[26];
    char temp='A'; 
    for(int i=0;i<n;i++)
    
     a[i]=temp;temp++;
    
    return a;

int main()

    int n=26;
    char *p=func(n);
    for(int i=0;i<n;i++)
     cout<<*(p+i)<<" ";

   //or you can also print like this
  
    for(int i=0;i<n;i++)
     cout<<p[i]<<" ";    


【讨论】:

【参考方案3】:

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

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

int* func()

  int* Arr = new int[100];
  return Arr;


int main()

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




【讨论】:

【参考方案4】:

这是一个需要解决的此类问题的完整示例

#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;

【讨论】:

【参考方案5】:

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

#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';

运行它,你会看到变化

【讨论】:

请使用正确的英文措辞(you'll 而不是 u'll)并省略诸如“buddy”之类的空短语。 另外:“那么实际上它是作为参考传递的”是错误的。变量y 本身是作为自身的副本传递的,但是因为它是一个指针,所以你将直接对数组进行操作。请编辑您的答案。 ***.com/questions/5573310/… TL;DR “因此,这两种形式是相同的。” 是的,从技术上讲,它是一个数组,你是对的,但复制的是指向数组的指针,而不是数组本身。【参考方案6】:

然后呢:

int (*func())

    int *f = new int[10] 1,2,3;

    return f;


int fa[10] =  0 ;
auto func2() -> int (*) [10]

    return &fa;

【讨论】:

【参考方案7】:

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

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

    如果要从函数返回一维数组,则必须声明一个返回指针的函数,如下例所示:
int * myFunction()    
   .
   .
   .

    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

【讨论】:

【参考方案8】:

这是一个相当老的问题,但我将投入 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;

这会将类型移动到预期的位置,使 far 更具可读性。当然,如果您只使用 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&lt;&gt; 技巧在被问到这个问题时是行不通的。那时您所希望的最好的结果就是在结构中使用类型:

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;

您可以传递结束指针,而不是传递大小,该指针将指向数组末尾之后的位置。这很有用,因为它使得一些更接近于 std 算法的东西,它接受一个开始和结束指针,但你现在返回的只是你必须记住的东西。

// 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&lt;int*, int*&gt;,你可以将它用于开始和结束,然后传递它,但它真的不再像一个数组了。

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)非常相似,但它们在这种情况下不起作用。

【讨论】:

【参考方案9】:

只需定义一个 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)

【讨论】:

这不是 C++【参考方案10】:
template<typename T, size_t N>
using ARR_REF = T (&)[N];

template <typename T, size_t N>
ARR_REF<T,N> ArraySizeHelper(ARR_REF<T,N> arr);

#define arraysize(arr) sizeof(ArraySizeHelper(arr))

【讨论】:

考虑改进您的答案。 Code-only answers may fall under 'Very Low Quality' ...and are candidates for deletion....We've always touted that we aren't a code factory. We are the people who teach others to fish. Code-only answers only feed a person for a day【参考方案11】:

最简单的方法是通过引用返回它,即使你不写 '&' 符号,通过引用自动返回

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

  

【讨论】:

【参考方案12】:

在 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;

【讨论】:

引用 OP:(...) you can consider the array returned arr2, totally another array (...)【参考方案13】:

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

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;

【讨论】:

【参考方案14】:

C++ 函数不能按值返回 C 样式的数组。最接近的是返回一个指针。此外,参数列表中的数组类型只是简单地转换为指针。

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

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

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 模板仅生成一个包含 C 样式数组的 struct,因此您可以应用面向对象的语义,同时保留数组的原始简单性。

【讨论】:

+1 举例说明如何通过引用传递数组。但是你错了,你不能通过引用返回一个数组。实现它的最简单的语法是使用 typedef:typedef int array[5]; array&amp; foo(); 但如果你想写这个,你甚至不需要 typedef:int (&amp;foo())[5] static int a[5] = ; return a; ,问题中的示例是:int (&amp;foo( int (&amp;a)[5] ))[5] return a; 。很简单,不是吗? @David:谢谢,我从 Comeau 消息error: function returning array is not allowed 中得到了误解,如果您在非 typedef 语法中省略了外部括号,就会发生这种情况。幸运的是,今天我检查了另一个问题的左右规则并设法构建了正确的东西......在看到你说有可能......在看到你给出代码之前:vP。 chubsdad 的答案从标准中得到了正确的引用:您不能返回数组,但可以返回指向数组的引用或指针。数组是不可复制的(作为一种类型),因此它们不能被返回——这意味着复制——并且当存在该语法时,编译器会将参数转换为指针。 @David:确实如此。这个页面变得异常长。从来没有这么多人自愿编写这么多琐碎的函数,在一个地方返回一个数组。 @Potatoswatter 我是cpp的新手,你能详细解释一下第二个代码sn-p吗?为了理解,我无法将其分解。【参考方案15】:

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

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;

在大多数情况下,使用*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);

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

【讨论】:

语法错误,&amp;符号必须出现在类型后面:void fillarr(std::vector&lt;int&gt; &amp; arr)【参考方案16】:

$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);

【讨论】:

你的第二个函数返回一个指向int的指针,而不是一个数组。 再次,为什么在函数内部更新实际数组时返回类型?这是最佳实践问题吗?【参考方案17】:

这个:

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)

这样,无论它是什么,您都可以使用长度将数组填充到它的长度。

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

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,更安全、更轻松。 (即使不是更快,也一样快。)【参考方案18】:

在这种情况下,您的数组变量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;

【讨论】:

澄清一下,“经典 C++ 语句”是错误的;数组不是指针。 记住 a[i] == *(a + i) 规则 @Brent Nash,没有。数组是数组。指向数组开头的指针是指针。碰巧编译器有一些语法糖可以在某些情况下为您进行翻译。 array&amp;array 在很多情况下可以互换。 @Brent:不。数组是它自己的类型,它不是一种特殊的指针。 int a[10] 中的a 的类型是int[10]。您可以说的是数组“衰减”为指向其第一个元素的指针。 (这是一个隐式的数组到指针的转换。)然后你的答案会和我的一样。如果您编辑答案以区分数组、数组到指针的转换和指针,我将删除我的答案,因为它们具有相同的核心信息,而您是第一个。 @seand 记住 a[i] == *(a + sizeof(a)*i) 规则【参考方案19】:
int *fillarr(int arr[])

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

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

【讨论】:

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

以上是关于在函数中返回数组的主要内容,如果未能解决你的问题,请参考以下文章

函数的返回值能不能是数组?在其他函数中怎么接收?

C语言函数中怎么返回一个二维数组

在存储函数中返回数组

C语言子函数如何返回一个数组,主函数如何接收?

C语言函数中返回数组

在 C++ 中返回多维数组的函数