Pass array by reference in C++

146 views Asked by At

I have a Fortran code in which I need to sort two arrays that are related to each other. I would like to do this in a C++ function in order to leverage the built-in sort algorithm in the STL. Because Fortran is pass-by-reference, all arguments to the C++ function must be pointers. The following function esort sorts the arrays correctly, but does not return the correct values. I believe this is because the pointers are passed by value, so the update at the end of the function has no effect. How should I change my code to achieve the desired effect?

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

void esort(double* lambda, double* vecs, int* n) {

  double* res_lam = new double[*n];
  double* res_vec = new double[*n * *n];
  vector<pair<double, int> > order(*n);

  for (int i=0; i<*n; i++) {
    order[i] = make_pair(lambda[i], i);
  }

  sort(order.rbegin(), order.rend());

  for (int i=0; i<*n; i++) {
    pair<double, int> p = order.at(i);
    res_lam[i] = p.first;
    for (int j=0; j<*n; j++) {
      res_vec[*n*i + j] = vecs[*n*p.second + j];
    }
  }

  lambda = res_lam;
  vecs = res_vec;

  delete [] res_lam;
  delete [] res_vec;

  return;
}

int main() {

  double lambda[] = {0.5, 2.0, 1.0};
  double vecs[] = {0.5, 0.5, 0.5, 2.0, 2.0, 2.0, 1.0, 1.0, 1.0};
  int n = 3;

  esort(lambda, vecs, &n);

  cout << "lambda" << endl;
  for (int i=0; i<n; i++) {
    cout << lambda[i] << " ";
  }
  cout << endl;
  cout << "vecs" << endl;
  for (int i=0; i<n; i++) {
    for (int j=0; j<n; j++) {
      cout << vecs[j*n + i] << " ";
    }
    cout << endl;
  }

  return 0;
}

Output:

lambda
0.5 2 1 
vecs
0.5 2 1 
0.5 2 1 
0.5 2 1

Desired Output:

lambda
0.5 1 2 
vecs
0.5 1 2 
0.5 1 2 
0.5 1 2

EDIT: The ith element of lambda corresponds to the ith column of vecs (in Fortran's column-major order). In order to avoid hassling with multi-dimensional arrays in C++, I am simply representing vecs as a 1D array within esort. The point of esort is to sort lambda and then reorder vecs such that the ith element of lambda still corresponds to the ith column of vecs.

EDIT 2: By placing cout statements inside esort, I have confirmed that res_lam and res_vec have the values that I want them to have at the end of the routine. My issue is getting those values returned to the calling program.

3

There are 3 answers

0
Derek T. Jones On BEST ANSWER

It is true that C++ passes all of its arguments by value, including pointers, meaning that assigning to lambda and vecs has no effect on the caller: those variables only point to the data. You need to copy your results into that pointed-to memory, as shown below by using copy from <algorithm>. Also rbegin and rend are reverse iterators, which causes sort to sort backward from what you wanted; I changed it to begin and end. I would further suggest using vector for your temporary res_lam and res_vec arrays.

#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>

using namespace std;

void esort(double* lambda, double* vecs, int* n) {

  vector<double> res_lam(*n);
  vector<double> res_vec(*n * *n);
  vector<pair<double, int> > order(*n);

  for (int i=0; i<*n; i++) {
    order[i] = make_pair(lambda[i], i);
  }

  sort(order.begin(), order.end());

  for (int i=0; i<*n; i++) {
    pair<double, int> p = order.at(i);
    res_lam[i] = p.first;
    for (int j=0; j<*n; j++) {
      res_vec[*n*i + j] = vecs[*n*p.second + j];
    }
  }

  copy(res_lam.begin(), res_lam.end(), lambda);
  copy(res_vec.begin(), res_vec.end(), vecs);
}
6
Ediac On

void esort(double* lambda, double* vecs, int* n)

When calling this function, it creates local pointer variables that reference the same memory address. In the code when the following is done:

lambda = res_lam;
vecs = res_vec;

It doesn't mean that you are changing the value of the memory address you passed as argument to the function, but rather the local pointer variable of the function lambda and vecs are now pointing to a different memory address.

0
TryinHard On

Alternatively you can use Pointer to Pointer Concept.

void esort(double** lambda, double** vecs, int* n)