How to convert between flat and multidimensional arrays without copying data?

Viewed 394

I've got some data structured as a multi-dimensional array, i.e. double[][], and I need to pass it to a function that expects a single linear array of double[] along with dimensional metadata for the multi-dimensional representation.

For example, I might have a 3 x 5 multidimensional array, which I need to pass as a 15-element flat array along with height and width parameters so that the function knows it is a 3x5 array rather than a 5x3 array.

The function will then return a flat array and size metadata, which I need to use to convert the data back into a multidimensional type.

I believe the data layout in memory is exactly the same for both the flat and multi-dimensional representations; the only difference is how the indexing operations are performed. So I'd like to do the "conversion" with typecasting rather than copying the array values.

What's the most correct and readable way to typecast between multidimensional and flat arrays of the same total size?

I actually know what the dimensions of the multi-dimensional array will be at compile time. The array sizes aren't dynamic.

4 Answers

The most correct way has been given by @Maxim Egorushkin and @ypnos: double *flat = &multi[0][0];. And it will work fine with any decent compiler. But unfortunately is not valid C++ code and invokes Undefined Bahaviour.

The problem is that for an array double multi[N][M]; (N and M being compile time contant expressions), &multi[0][0] is the address of the first element of an array of size M. So it is legal to do pointer arithmetics only up to M. See this other question of mine for more details.

What's the most correct and readable way to typecast between multidimensional and flat arrays of the same total size?

The address of the first array element coincides with the address of the array. You can pass around the address of the first element, no casting is necessary.

I would assume the most popular way to do it is:

double *flat = &multi[0][0];

This is how it is done in C, and you do operate with simple C arrays.

You could also have a look at std::array in your use case (dimensions known at compile time), but that one is not multi-dimensional, so if you would cascade it, you would lose the contiguous layout.

You can use cast to a reference to an array. This require to use some fancy C++ type syntax but in return it allows to use all features that work on arrays, like for each loop.

#include <iostream>

using namespace std;

int main()
{
    static constexpr size_t x = 5, y = 3;
    unsigned multiArray[x][y];
    for (size_t i = 0; i != x; ++i)
        for (size_t j = 0; j != y; ++j)
            multiArray[i][j] = i * j;

    static constexpr size_t z = x * y;
    unsigned (&singleArray)[z] = (unsigned (&)[z])multiArray[0][0];
    for (const unsigned value : singleArray)
        cout << value << ' ';
    cout << endl;

    return 0;
}

Take into account that this and other methods basing on casts work only with real multi-dimensional arrays. If it is an array of arrays (like unsigned **multiArray;), it isn't allocated in a continuous block of memory and a cast cannot bypass that.

Related