如何从一个函数中返回一个数组?

49

我该如何从一个方法中返回一个数组,以及如何声明这个数组?

int[] test(void); // ??

1
这个问题被关闭,因为它是一个与另一个问题非常相似的重复问题。然而,那个问题是关于返回作为函数参数传递的数组的。这是一个非常不同的命题,这意味着这是一个不同(并且更有趣)的问题。 - Martin Bonner supports Monica
您应该返回 std::arraystd::vector,而不是 C 风格数组。 - Jesper Juhl
5个回答

62

int* test();

然而,使用向量会更符合C++的风格:

std::vector< int > test();

编辑
我想澄清一些事情。既然您提到了C ++,我将使用new[]delete[]运算符,但malloc/free也是相同的。

在第一种情况下,您将编写类似于以下内容的代码:

int* test() {
    return new int[size_needed];
}

但这不是一个好主意,因为函数的客户端并不真正知道你返回的数组的大小,尽管客户端可以通过调用delete[]来安全地释放它。

int* theArray = test();
for (size_t i; i < ???; ++i) { // I don't know what is the array size!
    // ...
}
delete[] theArray; // ok.

一个更好的签名应该是这样的:
int* test(size_t& arraySize) {
    array_size = 10;
    return new int[array_size];
}

现在你的客户端代码将会是:

size_t theSize = 0;
int* theArray = test(theSize);
for (size_t i; i < theSize; ++i) { // now I can safely iterate the array
    // ...
}
delete[] theArray; // still ok.

由于这是C++,std::vector<T> 是一个广泛使用的解决方案:

std::vector<int> test() {
    std::vector<int> vector(10);
    return vector;
}

现在您不必调用delete[],因为它将由对象处理,并且您可以安全地迭代它:

std::vector<int> v = test();
std::vector<int>::iterator it = v.begin();
for (; it != v.end(); ++it) {
   // do your things
}

更加易于操作和更安全。


3
我会选择返回一个std::vector - Daniel Lidström
10
就我个人而言,如果你不解释它实际上并没有返回一个数组而是一个指针,那么回答“如何在C++方法中返回一个数组?”的问题时回复int* test();是具有误导性的。 - CB Bailey
2
当你将一个向量的引用传递给函数并且函数添加它想要的所有元素时,这不是更有效率吗?这样你就不必复制整个向量,包括所有元素(这就是当你返回一个向量时发生的事情,对吧?) - Kapichu
并不是因为该函数符合RVO优化的条件:请参见http://en.wikipedia.org/wiki/Return_value_optimization - Simone
1
@Kapichu 如果RVO失败,向量将被移动而不是复制。 - fredoverflow
显示剩余2条评论

18
这听起来像一个简单的问题,但在C++中有很多选项。首先,您应该优先考虑使用std::vector<>,它可以在运行时动态增长到遇到的任何元素数量,或者使用std::array<>(在C++11中引入),它始终在编译时存储指定数量的元素,因为它们会为您管理内存,确保正确的行为并极大地简化了事情:
std::vector<int> fn()
{
    std::vector<int> x;
    x.push_back(10);
    return x;
}

std::array<int, 2> fn2()  // C++11
{
    return {3, 4};
}

void caller()
{
    std::vector<int> a = fn();
    const std::vector<int>& b = fn(); // extend lifetime but read-only
                                      // b valid until scope exit/return

    std::array<int, 2> c = fn2();
    const std::array<int, 2>& d = fn2();
}

创建一个对返回数据的const引用的做法有时可以避免复制,但通常您可以依赖于返回值优化,或者 - 对于vector而不是array - 使用移动语义(在C++11中引入)。

如果你真的想使用内置数组(与上面提到的名为array的标准库类不同),一种方法是调用者预留空间并告诉函数使用它:

void fn(int x[], int n)
{
    for (int i = 0; i < n; ++i)
        x[i] = n;
}

void caller()
{
    // local space on the stack - destroyed when caller() returns
    int x[10];
    fn(x, sizeof x / sizeof x[0]);

    // or, use the heap, lives until delete[](p) called...
    int* p = new int[10];
    fn(p, 10);
}

另一种选择是将数组包装在一个结构体中,与原始数组不同的是,结构体可以合法地通过值从函数返回:
struct X
{
    int x[10];
};

X fn()
{
    X x;
    x.x[0] = 10;
    // ...
    return x;
}

void caller()
{
    X x = fn();
}

从上面开始,如果你被困在使用C++03的情况下,你可能想将其泛化为更接近C++11的std::array

template <typename T, size_t N>
struct array
{
    T& operator[](size_t n) { return x[n]; }
    const T& operator[](size_t n) const { return x[n]; }
    size_t size() const { return N; }
    // iterators, constructors etc....
  private:
    T x[N];
};

另一种选择是让被调用的函数在堆上分配内存:

int* fn()
{
    int* p = new int[2];
    p[0] = 0;
    p[1] = 1;
    return p;
}

void caller()
{
    int* p = fn();
    // use p...
    delete[] p;
}

为了简化堆对象的管理,许多C++程序员使用“智能指针”,确保在指向对象的指针离开其作用域时进行删除。随着C++11的出现:
std::shared_ptr<int> p(new int[2], [](int* p) { delete[] p; } );
std::unique_ptr<int[]> p(new int[3]);

如果你的代码停留在C++03,最好的选择是查看你的机器上是否有boost库:它提供了`boost::shared_array`。另一个选择是让`fn()`保留一些静态内存,尽管这不是线程安全的,并且意味着每次调用`fn()`都会覆盖任何保留先前调用指针的数据。话虽如此,对于简单的单线程代码,它可能很方便(而且快速)。
int* fn(int n)
{
    static int x[2];  // clobbered by each call to fn()
    x[0] = n;
    x[1] = n + 1;
    return x;  // every call to fn() returns a pointer to the same static x memory
}

void caller()
{
    int* p = fn(3);
    // use p, hoping no other thread calls fn() meanwhile and clobbers the values...
    // no clean up necessary...
}

1
我不相信 new[] int(10) 是一个有效的新表达式。你是不是想说 new int[10] - CB Bailey
@Charles:可能吧,我讨厌整天被困在C#里,然后试图在这里回答问题...啊啊啊... ;-) 真的让我头疼。 - Tony Delroy

10

在C++中,不可能从函数返回一个数组。根据8.3.5[dcl.fct]/6规定:

函数的返回类型不能是数组或函数[...]

最常见的替代方案是返回一个类类型的值,其中该类包含一个数组,例如:

struct ArrayHolder
{
    int array[10];
};

ArrayHolder test();

要返回静态或动态分配数组的第一个元素的指针,文档必须告知用户是否需要(以及如何)释放返回指针所指向的数组。例如:
int* test2()
{
    return new int[10];
}

int* test3()
{
    static int array[10];
    return array;
}

尽管可以返回数组的引用或指针,但这种语法更加复杂,没有任何实际优势,因此极为罕见。
int (&test4())[10]
{
        static int array[10];
        return array;
}

int (*test5())[10]
{
        static int array[10];
        return &array;
}

4

如果你想从函数中返回数组,必须确保这些值不会存储在堆栈上,因为当你离开函数时,它们将消失。

所以,要么将数组定义为静态的,要么分配内存(或者传递它,但你最初的尝试是使用 void 参数)。对于你的方法,我会像这样定义:

int *gnabber(){
  static int foo[] = {1,2,3}
  return foo;
}

-1
"如何在C++方法中返回一个数组,我该如何声明它?int[] test(void); ??"
template <class X>
  class Array
{
  X     *m_data;
  int    m_size;
public:
    // there constructor, destructor, some methods
    int Get(X* &_null_pointer)
    {
        if(!_null_pointer)
        {
            _null_pointer = new X [m_size];
            memcpy(_null_pointer, m_data, m_size * sizeof(X));
            return m_size;
        }
       return 0;
    }
}; 

只适用于整数

class IntArray
{
  int   *m_data;
  int    m_size;
public:
    // there constructor, destructor, some methods
    int Get(int* &_null_pointer)
    {
        if(!_null_pointer)
        {
            _null_pointer = new int [m_size];
            memcpy(_null_pointer, m_data, m_size * sizeof(int));
            return m_size;
        }
       return 0;
    }
}; 

例子

Array<float> array;
float  *n_data = NULL;
int     data_size;
if(data_size = array.Get(n_data))
{     // work with array    }

delete [] n_data;

int类型的示例

IntArray   array;
int       *n_data = NULL;
int        data_size;
if(data_size = array.Get(n_data))
{  // work with array  }

delete [] n_data;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接