Alex319
Alex319

Reputation: 3908

Converting multidimensional arrays to pointers in c++

I have a program that looks like the following:

double[4][4] startMatrix;
double[4][4] inverseMatrix;
initialize(startMatrix) //this puts the information I want in startMatrix

I now want to calculate the inverse of startMatrix and put it into inverseMatrix. I have a library function for this purpose whose prototype is the following:

void MatrixInversion(double** A, int order, double** B)

that takes the inverse of A and puts it in B. The problem is that I need to know how to convert the double[4][4] into a double** to give to the function. I've tried just doing it the "obvious way":

MatrixInversion((double**)startMatrix, 4, (double**)inverseMatrix))

but that doesn't seem to work. Is that actually the right way to do it?

Upvotes: 23

Views: 34367

Answers (8)

trieck
trieck

Reputation: 61

You can definitely do something like the code below, if you want.

template <typename T, int n>
class MatrixP
{
public:
    MatrixP operator()(T array[][n])
    {
        for (auto i = 0; i < n; ++i) {
            v_[i] = &array[i][0];
        }

        return *this;
    }

    operator T**()
    {
        return v_;
    }

private:
    T* v_[n] = {};
};

void foo(int** pp, int m, int n)
{
    for (auto i = 0; i < m; ++i) {
        for (auto j = 0; j < n; ++j) {
            std::cout << pp[i][j] << std::endl;
        }
    }
}

int main(int argc, char** argv)
{
    int array[2][2] = { { 1, 2 }, { 3, 4 } };
    auto pa = MatrixP<int, 2>()(array);

    foo(pa, 2, 2);
}

Upvotes: 0

Cloud Cho
Cloud Cho

Reputation: 1774

There is a solution using the pointer to point by bobobobo

William Sherif (bobobobo) used the C version and I just want to show C++ version of bobobobo's answer.

int numRows = 16 ;
int numCols = 5 ;
int **a ;

a = new int*[ numRows* sizeof(int*) ];
for( int row = 0 ; row < numRows ; row++ )
{
    a[row] = new int[ numCols*sizeof(int) ];
}

The rest of code is the same with bobobobo's.

Upvotes: 0

AnT stands with Russia
AnT stands with Russia

Reputation: 320391

No, there's no right way to do specifically that. A double[4][4] array is not convertible to a double ** pointer. These are two alternative, incompatible ways to implement a 2D array. Something needs to be changed: either the function's interface, or the structure of the array passed as an argument.

The simplest way to do the latter, i.e. to make your existing double[4][4] array compatible with the function, is to create temporary "index" arrays of type double *[4] pointing to the beginnings of each row in each matrix

double *startRows[4] = { startMatrix[0], startMatrix[1], startMatrix[2] , startMatrix[3] };
double *inverseRows[4] = { /* same thing here */ };

and pass these "index" arrays instead

MatrixInversion(startRows, 4, inverseRows);

Once the function finished working, you can forget about the startRows and inverseRows arrays, since the result will be placed into your original inverseMatrix array correctly.

Upvotes: 30

UncleBens
UncleBens

Reputation: 41331

For given reason that two-dimensional array (one contiguous block of memory) and an array of pointers (not contiguous) are very different things, you can't pass a two-dimensional array to a function working with pointer-to-pointer.

One thing you could do: templates. Make the size of the second dimension a template parameter.

#include <iostream>

template <unsigned N>
void print(double a[][N], unsigned order)
{
    for (unsigned y = 0; y < order; ++y) {
        for (unsigned x = 0; x < N; ++x) {
            std::cout << a[y][x] << ' ';
        }
        std::cout << '\n';
    }
}

int main()
{
    double arr[3][3] = {{1, 2.3, 4}, {2.5, 5, -1.0}, {0, 1.1, 0}};
    print(arr, 3);
}

Another, a bit clumsier way might be to make the function accept a pointer to a single-dimensional array, and both width and height given as arguments, and calculate the indexes into a two-dimensional representation yourself.

#include <iostream>

void print(double *a, unsigned height, unsigned width)
{
    for (unsigned y = 0; y < height; ++y) {
        for (unsigned x = 0; x < width; ++x) {
            std::cout << a[y * width + x] << ' ';
        }
        std::cout << '\n';
    }
}

int main()
{
    double arr[3][3] = {{1, 2.3, 4}, {2.5, 5, -1.0}, {0, 1.1, 0}};
    print(&arr[0][0], 3, 3);
}

Naturally, a matrix is something that deserves a class of its own (but the above might still be relevant, if you need to write helper functions).

Upvotes: 7

Test
Test

Reputation: 1727

by nice coding if c++:

struct matrix {
    double m[4][4];
};

matrix startMatrix;
matrix inverseMatrix;

so the interface would be

void MatrixInversion(matrix &A, int order, matrix &B);

and use it

MatrixInversion(startMatrix, 4, inverseMatrix);

The benefit

  1. the interface is very simple and clear.
  2. once need to modify "m" of matrix internally, you don't need to update the interface.

Or this way

struct matrix {
    void Inversion(matrix &inv, int order) {...}
protected:
    double m[4][4];
};

matrix startMatrix;
matrix inverseMatrix;
...

An ugly way in c

void MatrixInversion(void *A, int order, void *B);
MatrixInversion((void*)startMatrix, 4, (void*)inverseMatrix);

EDIT: reference code for MatrixInversion which will not crash:

void MatrixInversion(void *A, int order, void *B)
{
    double _a[4][4];
    double _b[4][4];

    memcpy(_a, A, sizeof _a);
    memcpy(_b, B, sizeof _b);
    // processing data here

    // copy back after done
    memcpy(B, _b, sizeof _b);
}

Upvotes: -2

James Eichele
James Eichele

Reputation: 119134

Since you are using C++, the proper way to do something like this would be with a custom class and some templates. The following example is rather rough, but it gets the basic point across.

#include <iostream>

using namespace std;

template <int matrix_size>
class SquareMatrix
{
    public:
        int size(void) { return matrix_size; }
        double array[matrix_size][matrix_size];
        void copyInverse(const SquareMatrix<matrix_size> & src);
        void print(void);
};

template <int matrix_size>
void SquareMatrix<matrix_size>::copyInverse(const SquareMatrix<matrix_size> & src)
{
    int inv_x;
    int inv_y;

    for (int x = 0; x < matrix_size; x++)
    {
        inv_x = matrix_size - 1 - x;
        for (int y = 0; y < matrix_size; y++)
        {
            inv_y = matrix_size - 1 - y;
            array[x][y] = src.array[inv_x][inv_y];
        }
    }
}

template <int matrix_size>
void SquareMatrix<matrix_size>::print(void)
{
    for (int y = 0; y < 4; y++)
    {
        for (int x = 0; x < 4; x++)
        {
            cout << array[x][y] << " ";
        }   
        cout << endl;
    }
}

template <int matrix_size>
void Initialize(SquareMatrix<matrix_size> & matrix);

int main(int argc, char * argList[])
{
    SquareMatrix<4> startMatrix;
    SquareMatrix<4> inverseMatrix;

    Initialize(startMatrix);

    inverseMatrix.copyInverse(startMatrix);

    cout << "Start:" << endl;
    startMatrix.print();

    cout << "Inverse:" << endl;
    inverseMatrix.print();

    return 0;
}

template <int matrix_size>
void Initialize(SquareMatrix<matrix_size> & matrix)
{
    for (int x = 0; x < matrix_size; x++)
    {
        for (int y = 0; y < matrix_size; y++)
        {
            matrix.array[x][y] = (x+1)*10+(y+1);
        }
    }
}

Upvotes: 2

David
David

Reputation: 2861

The problem is that a two-dimensional array is not the same as an array of pointers. A two-dimensional array stores the elements one row after another — so, when you pass such an array around, only a pointer to the start is given. The receiving function can work out how to find any element of the array, but only if it knows the length of each row.

So, your receiving function should be declared as void MatrixInversion(double A[4][], int order, double B[4][]).

Upvotes: -1

leiz
leiz

Reputation: 4042

Two dimensional array is not a pointer to pointer or something similar. The correct type for you startMatrix is double (*)[4]. For your function, the signature should be like:

MatrixInversion( double (*A)[4], int order, double (*B)[4] );

Upvotes: 1

Related Questions