Как наложить простой указатель на многомерный массив фиксированного размера?

У меня есть функция, которая берет указатель на массив с плавающей запятой. На основании других условий я знаю, что указатель фактически указывает на матрицу 2x2 OR 3x3. (на самом деле память первоначально была выделена как таковая, например, float M [2] [2]). Важно, чтобы я сделал это определение в теле функции, а не как аргумент функции.

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

}

Я знаю, что я мог бы использовать шаблоны и другие методы, чтобы лучше решить эту проблему. Мой вопрос действительно о том, как сделать такой бросок в комментарии ###. Работает на С++.

Ответы

Ответ 1

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

Ответ 2

float * может указывать на первый элемент массива поплавков и должен быть reinterpret_castable для этого типа массива. И результат этого приведения может указывать на первый элемент a float [][] и поэтому должен быть reinterpret_castable для этого типа и т.д. Вы должны иметь возможность составлять такие приведения и просто делать

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

Аргумент типа float ** не совпадает и не должен использоваться таким образом.

Чтобы избежать поведения undefined, указатель должен исходить из реального многомерного массива, и если float* используется напрямую, вы не можете получить доступ к более первой строке многомерной матрицы.

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
}

Учитывая float arr[2][2];, ничто не гарантирует, что &arr[0][1] + 1 совпадает с &arr[1][0], насколько я смог определить. Поэтому, хотя вы можете использовать одномерный массив как многомерный массив, выполнив f[i*width + j], вы не можете рассматривать многомерный массив как одномерный массив.

Лучше использовать C-тип времени компиляции типа С++ вместо того, чтобы просто полагаться на случайное прохождение неправильной вещи или выполнение неправильного reinterpret_cast. Чтобы получить безопасность типа с использованием необработанных массивов, вы должны использовать ссылки на нужный тип массива:

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

Если вы хотите передать массивы по значению, вы не можете использовать необработанные массивы и вместо этого должны использовать что-то вроде std:: array:

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

Ответ 3

Этот вид литья всегда более чист, и с ним легче справиться с разумным использованием typedef:

typedef float Matrix_t[2][2];

Matrix_t* someThingAsMatrix = (Matrix_t*) matrixReturnAsArray;

Если это С++, а не C, вы должны создать класс матрицы. (Или еще лучше, посмотрите на источник с открытым исходным кодом.)