tags:

views:

156

answers:

6

Lets say I have the following code.

double *return_array(void) {
   double foo[2];
   foo[0] = 5;
   foo[1] = 6;
   cout << foo << endl;
   cout << foo[0] << endl << foo[1] << endl;
   return foo;
}

double *bar = return_array()
cout << bar << endl;
cout << bar[0] << endl << bar[1] << endl;

Now, bar and foo are still the same pointer but what was there has changed completely. How can I get around this? Basically, I need to pass 6 or 9 doubles from a function. What should I do?

+1  A: 

Allocate the memory on the heap instead of the stack using the new keyword:

double *return_array(void) {
    double * foo = new double [2];
    foo[0] = 5;
    foo[1] = 6;
    return foo;
}

Then the code that calls the function will eventually have to free up the memory when it's done with it using delete:

double * foo = return_array();
// ...
delete [] foo;
yjerem
You should always delete in the same place you create memory. This will cause memory leaks down the road due to mis-use.
Kieveli
Agreed, although you could still follow that rule with the above function by creating a complementary function to free the memory. But I would usually just do it the way you did in your answer.
yjerem
... or you could return an auto_ptr and not worry about it =)
Kieveli
Is your answer equivalent to @Kieveli's answer? What exactly does new do?
vgm64
It would work the same. I think we posted at the same time, or I posted after Jeremy.
Kieveli
+1  A: 

Use new to allocate memory that isn't scoped to the function.

Don't forget to delete[] the memory when you're done with it.

Amber
A: 

You can allocate the result on the heap or, better yet, use std::vector.

Michael Aaron Safyan
+5  A: 

Use a vector.

std::vector<double> return_array(void) {
   std::vector<double> foo;
   foo.push_back(5);
   foo.push_back(6);
   cout << foo[0] << endl << foo[1] << endl;
   return foo;
}


This is a better way, So you avoid copying the vector :

void fillVector(std::vector<double>& vec)
{
    vec.push_back(5);
    vec.push_back(6);
}

int main()
{
    std::vector<double> vec;

    fillVector(vec);
}


Now, bar and foo are still the same pointer but what was there has changed completely.

Because foo is allocated on the stack of the function, it gets deallocatd when the function returns. So, bar is actually pointing no where!

AraK
+4  A: 

Typically you would pass in your pre-allocated memory into the function:

int barsize = 2;
double *bar = new double[barsize];
fill_array( bar, barsize );
cout << bar << endl;
cout << bar[0] << endl << bar[1] << endl;
delete [] bar;

void fill_array( double *foo, int foosize )
{
  if ( foosize < 2 )
    return;

  foo[0] = 5;
  foo[1] = 6;
  cout << foo << endl;
  cout << foo[0] << endl << foo[1] << endl;
}

The rule I use is... always allocate and delete memory in the same spot.

Or use a std::vector. They're nice =) I never use arrays anymore.

Kieveli
However, that only works if you know how much you're going to need to allocate before the function is called. If the size of the results can vary, passing in a pre-allocated space won't work.
Amber
@Dav: And that is why a lot of C functions have a pre-flight mode. Pass a NULL pointer and it returns the size you need. Allocate the memory and call again. Which is why we love C++ std::vector. The rule about allocating and deleting in the same spot is good one.
Martin York
Ya... switch to using vectors if you can.
Kieveli
If I had many options I'd be using python =)
vgm64
Yup. This was what I was looking for. Just took the double* bar declaration out of the function. Easy and understandable. Thanks.
vgm64
A: 

If you're always returning a fixed number of elements and are willing to use TR1 extensions (or Boost), I'd go with std::tr1::tuple rather than vector or an array. It's a template class, so you'll want to typedef it, along the lines of:

typedef std::tr1::tuple<double, double, double, double, double, double> six_tuple;

Use make_tuple() with the appropriate number (and type) of arguments to create the tuple your function returns, and access its contents with the templated get<N>() function, e.g.:

six_tuple foo = std::tr1::make_tuple(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
std::cout << std::tr1::get<0>(foo) << std::endl; // prints '1.0'

The compiler will introspect on the number and types of arguments to make_tuple(), and throw an error if you're trying to assign to the wrong sort of tuple.

I prefer this to having to manage the memory myself, but your mileage may vary.

Meredith L. Patterson
Clear as mud. The next developer to try to figure out what you were trying to do would add your name to a list of curse words.
Kieveli
Meredith L. Patterson
I can picture my question being asked by anyone learning the basics of C++. Your answer my be well accepted by a more seasoned programmer, but is overkill for this situation. Thanks for the detail, though.
vgm64
It wouldn't scare me personally - I have 11+ years of C++ experience. It would scare the other developers who have less experience. I would still have to look up the tuple because of its infrequent use, which would take more of my time. Then I would wonder why you didn't use the vector. Then I would look for your comment explaining why you wrote something in a way that 80-90% of other developers wouldn't have even considered. Dismissing your unorthodox coding as being my 'fear of code' is a dubious logical argument, and sadly not the first time I've heard it.
Kieveli