Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ casting static two-dimensional double array to double**

I have such matrix in my program:

double m[3][4] = 
    {
        {2, 4, 5, 7},
        {4, 5, 1, 12},
        {9, 12, 13, -4}
    };

And I'd like to cast it to double** type.

I've already tried simple double** a = (double**)m;, but it doesn't work (when I try to read any value, I get "Access violation reading location 0x00000000.", which means I'm trying to read from NULL adress.

I found almost working solution:

double *b = &m[0][0];
double **c = &b;

It works when I read field c[0][any] But same NULL adress reading problem occurs, when I try to read value from field c[1][0].

What is the proper way to cast my double m[3][4] array to type double**?

edit: You say that's impossible. So I'll change a problem a little bit. How can I pass two-dimensional double array as a parameter to a function? My function has prototype:

void calculate(double **matrix, int n); //where matrix size is always n by n+1 

And it's working fine with dynamically-allocated arrays. I doubt that only way to make it work is allocating new dynamical array and copy original static array one element by another...

like image 799
Greg Witczak Avatar asked Apr 02 '13 14:04

Greg Witczak


2 Answers

If you're always using arrays (no pointers) for initialization, and you are able to avoid the pointer stuff in your calculate function, you might consider the following option, which uses size deduction by templates.

template<int m, int n>
void doubleFunc(double (&mat)[m][n])
{
    for (auto i = 0; i < m; i++)
    {
        for (auto j = 0; j < n; j++)
        {
            std::cout << mat[i][j] << std::endl;
        }
    }
}

It worked during my quick test.

double m[3][4] = 
{
    {2, 4, 5, 7},
    {4, 5, 1, 12},
    {9, 12, 13, -4}
};

doubleFunc(m);
like image 139
DorianGrey Avatar answered Sep 27 '22 20:09

DorianGrey


When you write

double m[3][4] 
    {
        {2, 4, 5, 7},
        {4, 5, 1, 12},
        {9, 12, 13, -4}
    };

The compiler actually creates an array of doubles as if you had written

double _m[] = {2, 4, 5, 7, 4, 5, 1, 12, 9, 12, 13, -4};

However, thanks to C/C++ type system, the compiler remembers that m's type is double [3][4]. In particular it remembers the sizes 3 and 4.

When you write

m[i][j]

the compiler replaces it by

_m[i * 4 + j];

(The 4 comes from the second size in double [3][4].) For instance, m[1][2] == 1 and _m[1 * 4 + 2] == _m[6] == 1 as well.

As others said, a double** is a different type which doesn't carry the sizes with it. To consider double** a as a 3 x 4 matrix, a[0], a[1] and a[2] must be pointers to double (that is, double*) pointing to the first element of the corresponding row. You can achieve this with

double* rows[] = { &m[0][0], &m[1][0], &m[2][0] };
double** a = &rows[0];

A simple cast doesn't create the variable rows above. Let me present other alternative (but equivalent) ways to define rows

double* rows[] = { &m[0][0], &m[0][0] + 4, &m[0][0] + 2 * 4};
double* rows[] = { &_m[0], &_m[4], &_m[2 * 4]};

As you can see, only the second size (i.e. 4) is necessary. In general, for multi-dimensional arrays, all sizes but the first are required. For this reason a 1-dimensional array

double x[4] = { 1, 2, 3, 4 };

can be implicitly converted to a double*

double* y = x;

Using this fact we can also write

double* rows[] = { _m, _m + 4, _m + 2 * 4};

Indeed, _m is converted to a double* pointing to m[0]. Then, in _m + 4, _m is is converted to a double* pointing to m[0] and to this pointer it's added 4. Hence, _m + 4 is a pointer the fourth double following _m[0], which is _m[4] and so on.

So far I have explained why you cannot cast a double [3][4] (or any other sizes) to a double**. Now, I'shall show, in your particular case, how calculate can be defined.

template <int N>
void calculate(double (&m)[N][N+1]) {
   // use m as a double[N][N+1]
}

You call

calculate(m);

and the compiler will deduce the size N for you. In general (i.e, when the second dimension is not the N + 1) you can write

template <int N, int M>
void calculate(double (&m)[N][M]) {
   // use m as a double[N][M]
}
like image 40
Cassio Neri Avatar answered Sep 27 '22 22:09

Cassio Neri