How to return an array from a function?
Asked Answered
S

5

50

How can I return an array from a method, and how must I declare it?

int[] test(void); // ??
Sierrasiesser answered 24/11, 2010 at 7:16 Comment(2)
This question had been closed as a duplicate of a question with a very similar title. However that question was asking about returning an array which was passed as an argument to the function. That is a very different proposition, and means this is a different (and more interesting question.)Cotter
You return a std::array or std::vector. Not a C-style array.Miamiami
R
62

int* test();

but it would be "more C++" to use vectors:

std::vector< int > test();

EDIT
I'll clarify some point. Since you mentioned C++, I'll go with new[] and delete[] operators, but it's the same with malloc/free.

In the first case, you'll write something like:

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

but it's not a nice idea because your function's client doesn't really know the size of the array you are returning, although the client can safely deallocate it with a call to delete[].

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

A better signature would be this one:

int* test(size_t& arraySize) {
    array_size = 10;
    return new int[array_size];
}

And your client code would now be:

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.

Since this is C++, std::vector<T> is a widely-used solution:

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

Now you don't have to call delete[], since it will be handled by the object, and you can safely iterate it with:

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

which is easier and safer.

Reynaldoreynard answered 24/11, 2010 at 7:18 Comment(7)
I'd go with returning a std::vector.Stearin
Personally, I think that replying int* test(); to the question "howto return a array in a c++ method?"[sic] is misleading if you don't then explain that it doesn't actually return an array but a pointer.Epi
I added some needful clarification.Reynaldoreynard
Isn't it more efficient when you pass a reference of a vector to the function and the function then adds as many elements as it wants? You wouldn't have to copy the whole vector then, including all elements (which is what happens when you return a vector, right?)Paulsen
Not really since the function is eligible for RVO optimization: see en.wikipedia.org/wiki/Return_value_optimizationReynaldoreynard
@Paulsen And if RVO fails, the vector will be moved, not copied.Emerick
Using std::vector is actually a bad solution since you are actually copying objects, which is not only pointless but expensive. Plus, it's not compatible with C code. I'm surprised this solution got this many up-votes and is accepted. It really isn't!Precession
E
18

how can i return a array in a c++ method and how must i declare it? int[] test(void); ??

This sounds like a simple question, but in C++ you have quite a few options. Firstly, you should prefer...

  • std::vector<>, which grows dynamically to however many elements you encounter at runtime, or

  • std::array<> (introduced with C++11), which always stores a number of elements specified at compile time,

...as they manage memory for you, ensuring correct behaviour and simplifying things considerably:

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();
}

The practice of creating a const reference to the returned data can sometimes avoid a copy, but normally you can just rely on Return Value Optimisation, or - for vector but not array - move semantics (introduced with C++11).

If you really want to use an inbuilt array (as distinct from the Standard library class called array mentioned above), one way is for the caller to reserve space and tell the function to use it:

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

Another option is to wrap the array in a structure, which - unlike raw arrays - are legal to return by value from a function:

struct X
{
    int x[10];
};

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

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

Starting with the above, if you're stuck using C++03 you might want to generalise it into something closer to the 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];
};

Another option is to have the called function allocate memory on the heap:

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

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

To help simplify the management of heap objects, many C++ programmers use "smart pointers" that ensure deletion when the pointer(s) to the object leave their scopes. With C++11:

std::shared_ptr<int> p(new int[2], [](int* p) { delete[] p; } );
std::unique_ptr<int[]> p(new int[3]);

If you're stuck on C++03, the best option is to see if the boost library is available on your machine: it provides boost::shared_array.

Yet another option is to have some static memory reserved by fn(), though this is NOT THREAD SAFE, and means each call to fn() overwrites the data seen by anyone keeping pointers from previous calls. That said, it can be convenient (and fast) for simple single-threaded code.

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...
}
Emmuela answered 24/11, 2010 at 7:37 Comment(2)
I don't believe that new[] int(10) is a valid new expression. Did you mean new int[10] ?Epi
@Charles: probably, I hate being stuck in C# all bloody day then trying to answer something on here... ahhhh... ;-) Totally does my head in.Emmuela
E
10

It is not possible to return an array from a C++ function. 8.3.5[dcl.fct]/6:

Functions shall not have a return type of type array or function[...]

Most commonly chosen alternatives are to return a value of class type where that class contains an array, e.g.

struct ArrayHolder
{
    int array[10];
};

ArrayHolder test();

Or to return a pointer to the first element of a statically or dynamically allocated array, the documentation must indicate to the user whether he needs to (and if so how he should) deallocate the array that the returned pointer points to.

E.g.

int* test2()
{
    return new int[10];
}

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

While it is possible to return a reference or a pointer to an array, it's exceedingly rare as it is a more complex syntax with no practical advantage over any of the above methods.

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

int (*test5())[10]
{
        static int array[10];
        return &array;
}
Epi answered 24/11, 2010 at 7:47 Comment(0)
F
4

Well if you want to return your array from a function you must make sure that the values are not stored on the stack as they will be gone when you leave the function.

So either make your array static or allocate the memory (or pass it in but your initial attempt is with a void parameter). For your method I would define it like this:

int *gnabber(){
  static int foo[] = {1,2,3}
  return foo;
}
Frottage answered 24/11, 2010 at 7:27 Comment(0)
M
-1

"how can i return a array in a c++ method and how must i declare it? 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;
    }
}; 

just for int

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

example

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

delete [] n_data;

example for int

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

delete [] n_data;
Miniature answered 4/3, 2015 at 11:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.