dzoni
dzoni

Reputation: 3

Returning a pointer to dynamically allocated array with normal function and void function in C++

I'm very new to C++, having come from a Fortran 90+ and MATLAB background. I've been playing with pointers in C++, and I've stumbled upon something I can't quite explain. A search on Google and Stack Overflow hasn't really yielded an explanation.

This is an arbitrary example I've come up with, just from messing around, trying to understand how C++ works.

In essence, what I'm trying to do is allocate a dynamic array within a function, populate the array, and return a pointer to the array in the calling function. If I use a void function to return the pointer to the array, it doesn't work. If I do it as a normal function with a return, it works just fine. Let's call those Case 1 and Case 2, respectively.

I've put together an example of what I mean:

#include <iostream>
using namespace std;

// Prototypes
void populate(int rows, int cols, double* ptr_to_mat);
double* populate_return(const int rows, const int cols);

// Main function
int main()
{

  int rows, cols;

  cout << "Number of rows:    ";
  cin >> rows;
  cout << "Number of columns: ";
  cin >> cols;
  cout << endl;

  double* ptr_to_mat;

  populate(rows, cols, ptr_to_mat);

  cout << endl << "OUTPUT IN main FROM populate" << endl;

  for (int r = 0; r <= rows-1; r++)
    for (int c = 0; c <= cols-1; c++)
      cout << "mat(" << r << "," << c << ") = " << ptr_to_mat[c+cols*r] << endl;

  cout << endl << endl;

  ptr_to_mat = populate_return(rows, cols);

  cout << endl << "OUTPUT IN main FROM populate_return" << endl;

  for (int r = 0; r <= rows-1; r++)
    for (int c = 0; c <= cols-1; c++)
      cout << "mat(" << r << "," << c << ") = " << ptr_to_mat[c+cols*r] << endl;


  delete [] ptr_to_mat;

  return 0;
}

// Other functions

// Return pointer by modifying argument -- CASE 1
void populate(const int rows, const int cols, double* ptr_to_mat)
{

  double* internal_mat = new double[rows*cols];

  cout << "INPUT IN populate" << endl;

  for (int r = 0; r <= rows - 1; r++)
    for (int c = 0; c <= cols - 1; c++)
      {
        cout << "mat(" << r << "," << c << ") = "; 
        cin >> internal_mat[c+cols*r];
      }

  ptr_to_mat = internal_mat;

  cout << endl << "OUTPUT IN populate" << endl;

  for (int r = 0; r <= rows-1; r++)
    for (int c = 0; c <= cols-1; c++)
      cout << "mat(" << r << "," << c << ") = " << ptr_to_mat[c+cols*r] << endl;

}

// Return pointer using "return" -- CASE 2
double* populate_return(const int rows, const int cols)
{

  double* internal_mat = new double[rows*cols];

  cout << "INPUT IN populate_return" << endl;

  for (int r = 0; r <= rows - 1; r++)
    for (int c = 0; c <= cols - 1; c++)
      {
        cout << "mat(" << r << "," << c << ") = "; 
        cin >> internal_mat[c+cols*r];
      }

  cout << endl << "OUTPUT IN populate_return" << endl;

  for (int r = 0; r <= rows-1; r++)
    for (int c = 0; c <= cols-1; c++)
      cout << "mat(" << r << "," << c << ") = " << internal_mat[c+cols*r] << endl;

  return internal_mat;

}

The result of running the above code is the following:

Number of rows:    3
Number of columns: 2

CASE 1
INPUT IN populate
mat(0,0) = 1
mat(0,1) = 2
mat(1,0) = 3
mat(1,1) = 4
mat(2,0) = 5
mat(2,1) = 6

OUTPUT IN populate
mat(0,0) = 1
mat(0,1) = 2
mat(1,0) = 3
mat(1,1) = 4
mat(2,0) = 5
mat(2,1) = 6

OUTPUT IN main FROM populate
mat(0,0) = -1.72952e-41
mat(0,1) = -2.77962e-42
mat(1,0) = -2.77966e-42
mat(1,1) = -2.7797e-42
mat(2,0) = -6.02988e-42
mat(2,1) = -2.77979e-42


CASE 2
INPUT IN populate_return
mat(0,0) = 1
mat(0,1) = 2
mat(1,0) = 3
mat(1,1) = 4
mat(2,0) = 5
mat(2,1) = 6

OUTPUT IN populate_return
mat(0,0) = 1
mat(0,1) = 2
mat(1,0) = 3
mat(1,1) = 4
mat(2,0) = 5
mat(2,1) = 6

OUTPUT IN main FROM populate_return
mat(0,0) = 1
mat(0,1) = 2
mat(1,0) = 3
mat(1,1) = 4
mat(2,0) = 5
mat(2,1) = 6

My hypothesis is that it has to do with when the pointer is assigned in the calling function. So, in Case 2, it is assigned to the left-hand side before the information within the function is deallocated from memory, whereas in Case 1, that memory is deallocated before the pointer is assigned in the calling function, since the assignment happens on the right-hand side.

Does that sound plausible at all? If not, what's the reason Case 2 works, but Case 1 doesn't?

If I wanted to allocate more than one dynamic array in a function and return pointers to those arrays, I'd have to use a void function. What could/should be done in that case?

Upvotes: 0

Views: 931

Answers (3)

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

To set the pointer correctly in your void function you'll need to pass it by reference:

void populate(const int rows, const int cols, double*& ptr_to_mat) { // ...

Upvotes: 0

Gordon Childs
Gordon Childs

Reputation: 36072

In c++ arguments are passed by value, so in the first instance you are modifying a copy of the resulting pointer. Use a pointer to a pointer and set the result by by referencing it.

Or as the better answer recommends: use a reference. This isn't c!

Upvotes: 0

juanchopanza
juanchopanza

Reputation: 227390

The reason the void version doesn't work is that the pointer parameter is passed by value, so the function has its own copy. To get it to "work", you would have to pass a reference to a pointer:

void populate(int rows, int cols, double*& ptr_to_mat);
//                                       ^

Note that in real code you would tend to use an std::vector or some other type that manages its own resources, rather than leave resource management to the caller. For example,

#include <vector>

std::vector<double> populate(int rows, int cols);

Upvotes: 4

Related Questions