在函数中返回数组

Ism*_*ush 190 c++ arrays pointers return function

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

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

Bre*_*ode 180

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

int fillarr(int arr[])
Run Code Online (Sandbox Code Playgroud)

有点只是语法糖.你真的可以用它替换它,它仍然可以工作:

int fillarr(int* arr)
Run Code Online (Sandbox Code Playgroud)

所以从同样的意义上说,你想从函数返回的内容实际上是指向数组中第一个元素的指针:

int* fillarr(int arr[])
Run Code Online (Sandbox Code Playgroud)

你仍然可以像普通数组一样使用它:

int main()
{
  int y[10];
  int *a = fillarr(y);
  cout << a[0] << endl;
}
Run Code Online (Sandbox Code Playgroud)

  • 澄清一下,"经典的C++语句"是错误的; 数组不是指针. (43认同)
  • 记住a [i] ==*(a + i)规则 (23认同)
  • @Brent:不.数组是它自己的类型,它不是一种特殊的指针.`int a [10]`中`a`的类型是`int [10]`.你能说的是数组"衰变"成指向它们的第一个元素的指针.(这是一个隐式的数组到指针转换.)然后你的答案就像我的那样.如果您编辑答案以区分数组,数组到指针转换和指针,我将删除我的答案,因为它们将具有相同的核心信息而您是第一个. (20认同)
  • @Brent Nash,没有.数组是一个数组.指向数组开头的指针是指针.只是编译器有一些语法糖在某些情况下为你做翻译.在许多情况下,`array`和`&array`是可以互换的. (8认同)
  • @seand记住a [i] ==*(a + sizeof(a)*i)规则 (8认同)
  • +1 GMan,混淆两者是危险的. (3认同)
  • @Amir 没有这样的规则。`a[i]` 字面意思是 `*(a + i)`;你的代码是错误的。 (3认同)
  • @Amir,问题有C++标记,其中表达式的计算结果为false. (2认同)

Pot*_*ter 103

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

int *fillarr( int arr[] ) { // arr "decays" to type int *
    return arr;
}
Run Code Online (Sandbox Code Playgroud)

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

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

使用Boost或C++ 11,传递引用只是可选的,语法不那么令人费解:

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

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

  • +1用于给出如何通过引用传递数组的示例.但你错了,你不能通过引用返回一个数组.实现它的最简单的语法是使用typedef:`typedef int array [5]; array&foo();`但如果你想写这个,你甚至不需要typedef:`int(&foo())[5] {static int a [5] = {}; 返回; 问题中的例子是:`int(&foo(int(&a)[5]))[5] {return a; }`.简单,不是吗? (4认同)
  • @David:它确实如此.这个页面变得异常漫长.从来没有这么多人自愿写过如此多的琐碎功能在一个地方返回阵列. (3认同)
  • [chubsdad](http://stackoverflow.com/questions/3473438/c-return-array-in-a-function/3473570#3473570) 的答案有来自标准的正确引用:你不能返回一个数组,但是您可以返回指向数组的引用或指针。数组是不可复制的(作为一种类型),因此它们不能被返回——这意味着复制——并且当存在该语法时,编译器会将参数转换为指针。 (2认同)

Chu*_*dad 20

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

  • 你的第二个函数返回一个指向`int`的指针,而不是一个数组. (7认同)

cub*_*l42 18

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

  • 引用OP:`(...)你可以认为数组返回arr2,完全是另一个数组(...)` (2认同)

Sin*_*ion 14

答案可能取决于您计划如何使用该功能.对于最简单的答案,让我们决定而不是数组,你真正想要的是一个向量.向量很好,因为所有世界的外观都像无聊的普通值,你可以存储在常规指针中.我们将看看其他选项以及之后你想要它们的原因:

std::vector<int> fillarr( std::vector<int> arr ) {
    // do something
    return arr;
}
Run Code Online (Sandbox Code Playgroud)

这将完全符合您的预期.好处是std::vector确保一切都得到干净利落.缺点是,如果您的阵列很大,这会复制大量数据.实际上,它会复制数组的每个元素两次.首先它复制矢量,以便函数可以将它用作参数.然后它再次复制它以将其返回给调用者.如果您可以自己处理矢量,那么您可以更轻松地完成任务.(如果调用者需要将其存储在某种变量中以进行更多计算,它可以第三次复制它)

看起来你真正想要做的就是填充一个集合.如果您没有特定的理由返回集合的新实例,那么请不要.我们可以这样做

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

这样你就可以获得对传递给函数的数组的引用,而不是它的私有副本.您对参数所做的任何更改都会被调用者看到.如果你愿意,你可以返回对它的引用,但这不是一个好主意,因为它有点暗示你得到的东西与你传递的不同.

如果你确实需要一个新的集合实例,但是想要避免将它放在堆栈上(以及所需的所有复制),你需要为如何处理该实例创建某种契约.最简单的方法是使用智能指针,只要有人抓住它,就会保持引用的实例.如果它超出范围,它会干净利落地消失.这看起来像这样.

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

在大多数情况下,使用*myArr与使用普通香草矢量相同的工作.此示例还通过添加const关键字来修改参数列表.现在你得到一个引用而不复制它,但你不能修改它,所以调用者知道它将与函数之前相同.

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

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

如果你不习惯看到这种风格,使用它看起来有点奇怪.

vector<int> arr;
vector<int>::iterator foo = fillarr(arr.begin(), arr.end());
Run Code Online (Sandbox Code Playgroud)

foo现在'指向'修改的开头arr.

真正令人高兴的是,它在矢量上同样适用于普通C数组和许多其他类型的集合

int arr[100];
int *foo = fillarr(arr, arr+100);
Run Code Online (Sandbox Code Playgroud)

现在看起来非常像本问题其他地方给出的普通指针示例.


Mat*_*att 9

这个:

int fillarr(int arr[])
Run Code Online (Sandbox Code Playgroud)

实际上被视为:

int fillarr(int *arr)
Run Code Online (Sandbox Code Playgroud)

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

int * fillarr(int arr[]){
    // do something to arr
    return arr;
}
Run Code Online (Sandbox Code Playgroud)

它并没有真正返回一个数组.你正在返回一个指向数组地址开头的指针.

但请记住,当你传入数组时,你只会传入一个指针.因此,当您修改数组数据时,实际上是在修改指针所指向的数据.因此,在传入数组之前,您必须意识到已经在外部修改了结果.

例如

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

我建议你可以考虑像这样在你的fillarr函数中加一个长度.

int * fillarr(int arr[], int length)
Run Code Online (Sandbox Code Playgroud)

这样你可以使用长度来填充数组的长度,无论它是什么.

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

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.
Run Code Online (Sandbox Code Playgroud)

如果你想要做的就是将数组设置为某些默认值,请考虑使用内置的memset函数.

类似于:memset((int*)&arr,5,sizeof(int));

虽然我在谈论这个话题.你说你正在使用C++.看看使用stl向量.您的代码可能更强大.

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


小智 5

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

struct Marks{
   int list[5];
}
Run Code Online (Sandbox Code Playgroud)

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

typedef struct Marks marks;
marks marks_list;
Run Code Online (Sandbox Code Playgroud)

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

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

我们也可以返回数组.要返回数组,函数的返回类型应为结构类型,即标记.这是因为实际上我们正在传递包含数组的结构.所以最终的代码可能看起来像这样.

marks getMarks(){
 return marks_list;
}
Run Code Online (Sandbox Code Playgroud)


Adr*_*ian 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;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您坚持使用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;
}
Run Code Online (Sandbox Code Playgroud)

除了那看起来很难看,而且很难读。我现在使用一些东西来帮助解决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;
}
Run Code Online (Sandbox Code Playgroud)

这使其中一个会希望它是,使得这种类型远远更具可读性。当然,如果您只使用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;
}
Run Code Online (Sandbox Code Playgroud)

就像我说的那样,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;
}
Run Code Online (Sandbox Code Playgroud)

虽然看起来可能很丑陋,但至少仍然更具可读性,尽管在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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

当时,自由的功能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));
}
Run Code Online (Sandbox Code Playgroud)

要么

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

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

编辑

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

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

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

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

另外,您可以记录该函数仅包含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;
}
Run Code Online (Sandbox Code Playgroud)

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

您可以传递一个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];
}
Run Code Online (Sandbox Code Playgroud)

要么

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

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