How to cast simple pointer to a multidimensional-array of fixed size?

31,059

Solution 1

float (*somethingAsMatrix)[2] = (float (*)[2]) matrixReturnAsArray;

Solution 2

float * could point to the first element of an array of floats, and ought to be reinterpret_castable to that array type. And the result of that cast could point to the first element of a float [][] and so should be reinterpret_castable to that type, and so on. You ought to be able to compose such casts and just directly do

float (&arr)[2][2] = *reinterpret_cast<float (*)[2][2]>(matrixReturnAsArray);

An argument of the type float ** is not the same and should not be used this way.

To avoid undefined behavior the pointer must originate from an actual multi-dimensional array, and if the float* is used directly you cannot access more than the first row of the multi-dimensional matrix.

void foo(float *f) {
    f[3] = 10.;

    float (&arr)[2][2] = *reinterpret_cast<float (*)[2][2]>(f);
    arr[1][1] = 10.;
}

void main() {
    float a[2][2];
    foo(&a[0][0]); // f[3] = 10.; is undefined behavior, arr[1][1] = 10. is well defined

    float b[4];
    foo(&b[0]); // f[3] = 10.; is well-defined behavior, arr[1][1] = 10. is undefined
}

Given float arr[2][2]; nothing guarantees that &arr[0][1] + 1 is the same as &arr[1][0], as far as I have been able to determine. So although you can use a single dimensional array as a multi-dimensional array by doing f[i*width + j] you cannot treat a multi-dimensional array like a single dimensional array.

It's better to use C++'s compile-time type-safety instead of just relying on not accidentally passing the wrong thing or performing the wrong reinterpret_cast. To get type-safety using raw-arrays you should use references to the raw array type you want:

void foo(float (&f)[2][2]) {}
void foo(float (&f)[3][3]) {}

If you want to pass arrays by value you can't use raw arrays and should instead use something like std::array:

void foo(std::array<std::array<float,2>,2> f) {}
void foo(std::array<std::array<float,3>,3> f) {}

Solution 3

This sort of casting is always cleaner, and easier to deal with, with a judicious use of typedef:

typedef float Matrix_t[2][2];

Matrix_t* someThingAsMatrix = (Matrix_t*) matrixReturnAsArray;

If this is C++ and not C, though, you should create a matrix class. (Or better yet, look for an open source one.)

Share:
31,059
NoahR
Author by

NoahR

Updated on July 09, 2022

Comments

  • NoahR
    NoahR almost 2 years

    I have a function that takes a pointer to a floating point array. Based on other conditions, I know that pointer is actually pointing to a 2x2 OR 3x3 matrix. (in fact the memory was initially allocated as such, e.g. float M[2][2] ) The important thing is I want to make this determination in the function body, not as the function argument.

    void calcMatrix( int face, float * matrixReturnAsArray )
    {
        // Here, I would much rather work in natural matrix notation
        if( is2x2 )
        {
            // ### cast matrixReturnAsArray to somethingAsMatrix[2][2]
            somethingAsMatrix[0][1] = 2.002;
            // etc..
        }
        else if(is3x3)
        { //etc...
        }
    
    }
    

    I am aware that I could use templates and other techniques to better address this problem. My question is really about how to make such a cast at the ### comment. Working in C++.