2

If I have an array that looks like this:

int map[21][28] =
{
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

How can I create a smaller array consisting of the values inside that array..?
A bit like this:

int zoomedMap[7][7] =
{

    2, 2, 1, 1, 1, 1, 1,
    1, 0, 0, 0, 0, 0, 1,
    1, 0, 0, 0, 0, 0, 1,
    1, 0, 0, 0, 0, 0, 1,
    1, 0, 0, 0, 0, 0, 1,
    1, 0, 0, 0, 0, 0, 1,
    1, 1, 1, 1, 1, 1, 1
};

I don't know if this helps, but:

  • I know the exact dimensions of both arrays
  • I want it to be possible to move the smaller arrays position, preferably by just changing an X/Y value
3
  • 1
    If you want this to be efficient, and referential, you don't need an array; you need a pointer, an (X,Y) coordinate tuple, a width+height tuple, and some math. Commented Jan 13, 2013 at 10:07
  • I have no idea how to do all that. Could you post an answer with some pseudo-code or just actual code? Commented Jan 13, 2013 at 10:18
  • Sure thing. It's simple, and like a regular array in C/C++, it isn't going to stop you from shooting yourself, but it should at least give you an idea or two. Commented Jan 13, 2013 at 12:18

4 Answers 4

1

In what is hopefully obvious, your sub-array is literally nothing more than a "block" of data (with some interesting partitioning) within your main array. Assuming you are checking your boundaries and will NOT allow an out of bounds condition to happen, you can simulate your sub-array using pointer math and some basic info about your main array.

  • The block will always be some [row][column] offset from the [0][0] location, so we need those values (row and column).
  • The block has fixed width such that column+width does not exceed your main array width. We need that width.
  • The block has fixed height such that row+height does not exceed your main array height. We need that height.
  • I hope it obvious we need the base address that is the main array (in your case map).

This is probably best demonstrated by example. The following is NOT some end-all solution. It will likely not even address 1/10th of the need you're going to have. Rather, it is intended to offer you an idea on how you can do this using only a pointer, some offsets, some sizes, and a little arithmetic to get what you're looking for. There is nothing stopping you from exceeding limits that may be harmful (just like a plain array), so be cautious.

// internal rerefential to a submatrix in a larger fixed matrix.
template<typename T>
class Sub2D
{
public:
    template<size_t R, size_t C>
    Sub2D(T(&ar)[R][C], int top, int left, int height, int width)
    : parent(ar[0])
    , row(top)
    , col(left)
    , max_row(R)
    , max_col(C)
    {
        if ((row+width) >= R || (col+height) >= C)
            throw std::out_of_range("");
    }

    // retrieve our subrow offset into the main 2D array
    T* operator [](size_t n)
    {
        // enable at your desire, but as Alex pointed out, all
        // the standard containers let you shoot yourself in the
        // foot with this operator. why not this one too =P
        //if (row+n >= max_row)
        //    throw std::out_of_range("");

        return parent + ((row+n)*max_col + col);
    }

private:
    T* parent;
    size_t row, col;
    size_t max_row, max_col;
};

Used like this, assuming your array in your question is the one we're basing:

int main()
{
    // take the submatrix & [6][7] that is 7x7 in dimension.
    Sub2D<int> sub(map, 6,7, 7,7);
    for (size_t i=0;i<7;++i)
    {
        for (size_t j=0;j<7;++j)
            cout << sub[i][j] << ' ';
        cout << endl;
    }
    cout << endl;

    // update an element at location [1][1] of our sub-matrix.
    sub[1][1] = 9;

    // reprint the *entire* main array. it better have updated.
    for (size_t i=0;i<sizeof(map)/sizeof(map[0]);++i)
    {
        for (size_t j=0;j<sizeof(map[0])/sizeof(map[0][0]);++j)
            cout << map[i][j] << ' ';
        cout << endl;
    }
    cout << endl;

    return 0;
}

Produces the following output.

2 2 1 1 1 1 1 
1 0 0 0 0 0 1 
1 0 0 0 0 0 1 
1 0 0 0 0 0 1 
1 0 0 0 0 0 1 
1 0 0 0 0 0 1 
1 1 1 1 1 1 1 

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 2 2 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 9 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

Note the 9 now sitting right where we expect it.

There is obviously a LOT more you can add to such a template class, including better range checking, moving your pointer, snap-shotting into other memory buffers, etc, but the point is for basic get-me-this sub matrix a pointer and a few offsets are hard to beat, especially for performance.

Sign up to request clarification or add additional context in comments.

2 Comments

Looks good, but I don't think operator[] should throw an exception, leave that to at(). operator[] should be unsafe, but efficient.
@AlexChamberlain good point about []. Updated shortly. Like i said. really anorexic implementation.
0

You could always do it statically with loops:

const int pos_x = 6, pos_y = 7, size_x = 7, size_y = 7;

int newMap[size_x][size_y] = {0};

for( int i = pos_x; i != pos_x+size_x; ++i ) {
    for( int j = pos_y; j != pos_y+size_y; ++j) {
        newMap[i-pos_x][j-pos_y] = map[i][j];
    }
}

Although this isn't very safe, and difficult to get working dynamically. You might want to either write a matrix-wrapper class with the functions you desire; or find one that already exists and extend it (I'd start looking into the STL or Boost for an existing matrix class)

Comments

0

Have a read of Boost's uBLAS module. It provides Matrix and MatrixRange classes, which are exactly what you are looking for.

In essence, the zoomedMap should just be an intelligent pointer into the larger matrix, which understands how to dereference and get the correct result.

1 Comment

So, you have a deep understanding of column-major or row-major ordering of matrices? You understand the limitations on C++ pointers when used in matrix operations?
0

Depending on your requirements, this may suffice:

int (*zoomedMap)[28] = reinterpret_cast<int (*)[28]>(&map[X][Y]);

This gives you an identifier you can use as if it were a two-dimensional array: zoomedMap[i][j]. You can easily move the zoomed map within the larger array by assigning a new address to it.

It depends on your C++ implementation allowing a pointer to float to be cast to a pointer to an array of float, provided all references remain within the original array. This is common.

It leaves zoomedMap pointing to the same memory as map. Thus, you cannot change one without changing the other. If you want to do that, you would need to make a copy.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.