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

纵然是瞬间 提交于 2019-12-21 04:21:02

问题


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


回答1:


You can't just cast the array. You are going to have to create something like this:

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

double *marray[3] = {m[0],m[1],m[2]};
calculate(marray,3);

Or you can use a loop:

const size_t n = 3;
double *marray[n];
for (size_t i=0; i!=n; ++i) {
    marray[i] = m[i];
}
calculate(marray,n);



回答2:


You can't.

The notation double** refers to an array of pointers. You don't have an array of pointers, you have an array of arrays of doubles.




回答3:


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]
}



回答4:


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



回答5:


You can pass a 2d array as a function parameter:

void calculate(double matrix[][y], int n);




回答6:


Until variable-length arrays are in the C++ standard, your choices include:

  • If your compiler supports variable-length arrays as an extension, you can likely pass them with a function declaration such as void foo(std::size_t n, double a[][n+1]). Note that the compiler likely requires n to be passed before a parameter declaration that uses n or requires some special syntax.
  • You can pass a double * and do index arithmetic manually in the function: void foo(std::size_t n, double *a) { … a[row*(n+1) + column] … }
  • You can create a class the implements variable-length arrays by doing the index arithmetic in its accessor functions.
  • You can allocate space for n pointers to double, fill those pointers with pointers to each row of the array, and pass the address of the space.


来源:https://stackoverflow.com/questions/15766382/c-casting-static-two-dimensional-double-array-to-double

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!