20

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?

Alex319
  • 3,818
  • 9
  • 34
  • 40
  • &startMatrix, 4, &inverseMatrix – James Black Oct 18 '09 at 05:37
  • 1
    Why don't you make a matrix class, rather doing things the C way? (Passing objects into functions, rather then invoking methods on objects) – GManNickG Oct 18 '09 at 05:40
  • 3
    @GMan: Since the OP said that the function is "a library function", he most likely has no freedom to change the interface. Class or no class, at some point he'll have to get the proper 'double **' to pass to the function. – AnT stands with Russia Oct 18 '09 at 06:03
  • possible duplicate of [conversion of 2D array to pointer-to-pointer](http://stackoverflow.com/questions/8203700/conversion-of-2d-array-to-pointer-to-pointer) – kiranpradeep Apr 17 '15 at 16:20

8 Answers8

28

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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
7

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).

UncleBens
  • 40,819
  • 6
  • 57
  • 90
2

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);
        }
    }
}
e.James
  • 116,942
  • 41
  • 177
  • 214
  • i like your idea, the correct c++ way. a good example regardless of works or not. – Test Oct 18 '09 at 06:51
  • Also good to take in a template type T, which is what the elements will be. And probably make size unsigned, since negative sizes don't make sense. – GManNickG Oct 18 '09 at 07:03
  • 1
    Yet implementing a relatively heavy operation (it's inverse, BTW, not transpose) as a template function parametrized by matrix size might result in code bloat, since the code will be re-instantiated for each particular size. The proper technique of dealing with this issue is to implement the bulk of the functionality by a function parametrized with a run-time size (as in the original question) and then build a "thin" template on top of that function. But that brings us back to the original problem. – AnT stands with Russia Oct 18 '09 at 07:52
1

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] );
leiz
  • 3,984
  • 2
  • 23
  • 17
  • 2
    The function is apparently working with square matrices of any size (order). Restricting it to 4x4 matrices only is hardly acceptable. Also, there's no point to pass the 'order' now. – AnT stands with Russia Oct 18 '09 at 05:44
  • @AndreyT, I was just showing him/her how to do it. If I wanted to it be general, I could show him how to have a class that represents a matrix. – leiz Oct 18 '09 at 05:54
  • In a *square* matrix, row count is the same as column count. You hardcoded your column count as 4. Now there's no point to pass the row count anymore - it also must be 4 and only 4. – AnT stands with Russia Oct 18 '09 at 05:57
  • The OP called the function "a library function". This normally means that there's no freedom to change the function's interface. – AnT stands with Russia Oct 18 '09 at 06:02
  • @AndreyT, ok, I did NOT see that it was a library function. – leiz Oct 18 '09 at 06:14
0

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.

Cloud Cho
  • 1,594
  • 19
  • 22
0

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);
}
trieck
  • 51
  • 3
-1

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][]).

David
  • 2,821
  • 20
  • 16
  • 1
    If you know the sizes of the arrays and you know how it was filled in, you can pass it as &A, but you have a great deal that you need to know ahead of time, but it is possible to do. – James Black Oct 18 '09 at 05:46
  • This is not a correct declaration. Only the first size can be omitted in the array parameter declaration. Also, fixing the size of the matrix kills the intended felxibility of the function (i.e its ability to process matrix of any size). – AnT stands with Russia Oct 18 '09 at 05:49
  • @James Black: Exactly. If we were going to fix the size of the square matrix anyway, the proper way to declare the parameter would be `double (&A)[4][4]`, not `double A[][4]`. A least it would preserve the full array type, instead of decaying it to `double (*)[4]`. – AnT stands with Russia Oct 18 '09 at 06:07
-2

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);
}
Test
  • 1,697
  • 1
  • 11
  • 10
  • The same design error as in some other responses... You hardcoded the fixed matrix size in your matrix type. What are you going to do if you'd need a 5x5 matrix? 6x6? And what is the point of passing 4 to the function, when 4 is already hardcoded into the matrix type? – AnT stands with Russia Oct 18 '09 at 06:19
  • Your "ugly way in C" will simply crash the program. The OP already tried it, if you noticed, and it didn't work (for obvious reasons). – AnT stands with Russia Oct 18 '09 at 06:21
  • refactorying rule: improve design when necessary. see "The benefig" section. "4" is a specific example, that is, an integer, and its value is 4. – Test Oct 18 '09 at 06:23
  • actually, wont crash because you got known already a 2d double array is not just double**, so, you'll could process it safely. see my reference code. – Test Oct 18 '09 at 06:35
  • @AndreyT, you disappointed me, you just know nothing. if have trouble, please yourself write some code to do a test, not just say A is bad, B is not good. – Test Oct 18 '09 at 06:40
  • Sorry, I have hard time understnding what you are trying to say. Again, the OP explictly stated in his question that what you suggest in your "ugly way in C" does not work. A just repeated it to you, since for some reason you decided to repost it. Most everybody here knows and understands that it won't work and why it won't work. – AnT stands with Russia Oct 18 '09 at 06:47
  • You still haven't explained how you are going to work with, say, a 5x5 matrix and why you are passing the 'order' when the order is fixed at compile time. – AnT stands with Russia Oct 18 '09 at 06:49
  • 1. repost: found double** is not necessary at all. 2. various dimensional size: that not my point. while if i SHOULD give a proposal, please consider configurable object because we are talking about c++ here (sorry but do you know what is a configurable object?). though if you think the "C library" rule cannot be broken, adding parameters which indicate the size should be enough. – Test Oct 18 '09 at 07:01
  • Sorry, but what I see so far is a growing pile up of irrelevant code. Please, re-read the original question. – AnT stands with Russia Oct 18 '09 at 07:21
  • @AndreyT, well, your are very interesting. OP said "The problem is that I need to know how to convert the double[4][4] into a double** to give to the function", and the question is equal to: "why cannot use double** on the MatrixInversion interface corresponding to a double startMatrix[4][4] array?". almost every body gave the correct explain including you. so what are you arguing with every body for? showing that your are a idealism? – Test Oct 18 '09 at 08:25