views:

455

answers:

8

How do you dynamically allocate a 2D matrix in C++? I have tried based on what I already know:

#include <iostream>

int main(){
    int rows;
    int cols;
    int * arr;
    arr = new int[rows][cols];
 }

It works for one parameter, but now for two. What should I do?

+7  A: 

A matrix is actually an array of arrays.

int rows = ..., cols = ...;
int** matrix = new int*[rows];
for (int i = 0; i < rows; ++i)
    matrix[i] = new int[cols];

Of course, to delete the matrix, you should do the following:

for (int i = 0; i < rows; ++i)
    delete [] matrix[i];
delete [] matrix;


I have just figured out another possibility:

int rows = ..., cols = ...;
int** matrix = new int*[rows];
if (rows)
{
    matrix[0] = new int[rows * cols];
    for (int i = 1; i < rows; ++i)
        matrix[i] = matrix[0] + i * cols;
}

Freeing this array is easier:

if (rows) delete [] matrix[0];
delete [] matrix;

This solution has the advantage of allocating a single big block of memory for all the elements, instead of several little chunks. The first solution I posted is a better example of the arrays of arrays concept, though.

Eduardo León
That's not a bad idea. It has the obvious advantage of maintaining cache locality while still keeping the multidimensional array syntax. I like it.
greyfade
+1  A: 
 #include <iostream>

    int main(){
        int rows=4;
        int cols=4;
        int **arr;

        arr = new int*[rows];
        for(int i=0;i<rows;i++){
           arr[i]=new int[cols];
        }
        // statements

        for(int i=0;i<rows;i++){
           delete []arr[i];
        }
        delete []arr;
        return 0;
     }
adatapost
+1  A: 

or you can just allocate a 1D array but reference elements in a 2D fashion:

to address row 2, column 3 (top left corner is row 0, column 0):

arr[2 * MATRIX_WIDTH + 3]

where MATRIX_WIDTH is the number of elements in a row.

DmitryK
Why would you try to hack up a two-dimensional array when it's perfectly easy to make a real two-dimensional array?
Chris Lutz
because you can access the two-dimensional way in a cache friendly manner when performance is really key
DeusAduro
Even better: allocate a 1D array of elements and another 1D array of pointers to the first element of every row.
Eduardo León
+1  A: 
arr = new int[cols*rows];

If you either don't mind syntax

arr[row * cols + col] = Aij;

or use operator[] overaloading somewhere. This may be more cache-friendly than array of arrays, or may be not, more probably you shouldn't care about it. I just want to point out that a) array of arrays is not only solution, b) some operations are more easier to implement if matrix located in one block of memory. E.g.

for(int i=0;i < rows*cols;++i)
   matrix[i]=someOtherMatrix[i];

one line shorter than

for(int r=0;i < rows;++r)
  for(int c=0;i < cols;++s)
     matrix[r][c]=someOtherMatrix[r][c];

though adding rows to such matrix is more painful

maykeye
+9  A: 

You can also use std::vectors for achieving this:

using std::vector< std::vector<int> >

Example:

std::vector< std::vector<int> > a;

  //m * n is the size of the matrix

    int m = 2, n = 4;
    //Grow rows by m
    a.resize(m);
    for(int i = 0 ; i < m ; ++i)
    {
        //Grow Columns by n
        a[i].resize(n);
    }
    //Now you have matrix m*n with default values

    //you can use the Matrix, now
    a[1][0]=1;
    a[1][1]=2;
    a[1][2]=3;
    a[1][3]=4;

//OR
for(i = 0 ; i < m ; ++i)
{
    for(int j = 0 ; j < n ; ++j)
    {      //modify matrix
        int x = a[i][j];
    }

}
aJ
+1 for using std::vector. I don't use the STL too much myself, but that's because I'm a masochist when it comes to memory management. Using the STL is way less error prone.
Eduardo León
While this works, it has a repeated manual overhead. You a prepared class (there are many) or wrap up c-style n-dimensional arrays in a class of your own devising.
dmckee
Instead of resizing just construct it with the correct size. `int m = 10, n = 4; std::vector< std::vector<int> > a(m, std::vector<int>(n,0));`
TimW
+9  A: 

Try boost::multi_array

#include <boost/multi_array.hpp>

int main(){
    int rows;
    int cols;
    boost::multi_array<int, 2> arr(boost::extents[rows][cols] ;
}
yoco
+1 for using Boost, but using such a library depends on how good the compiler you use is at optimizing your code.
Eduardo León
A: 

The other answer describing arrays of arrays are correct.
BUT if you are planning of doing a anything mathematical with the arrays - or need something special like sparse matrices you should look at one of the many maths libs like TNT before re-inventing too many wheels

Martin Beckett
A: 

I have this grid class that can be used as a simple matrix if you don't need any mathematical operators.

/**
 * Represents a grid of values.
 * Indices are zero-based.
 */
template<class T>
class GenericGrid
{
 public:
  GenericGrid(size_t numRows, size_t numColumns);

  GenericGrid(size_t numRows, size_t numColumns, const T & inInitialValue);

  const T & get(size_t row, size_t col) const;

  T & get(size_t row, size_t col);

  void set(size_t row, size_t col, const T & inT);

  size_t numRows() const;

  size_t numColumns() const;

 private:
  size_t mNumRows;
  size_t mNumColumns;
  std::vector<T> mData;
};


template<class T>
GenericGrid<T>::GenericGrid(size_t numRows, size_t numColumns):
 mNumRows(numRows),
 mNumColumns(numColumns)
{
 mData.resize(numRows*numColumns);
}


template<class T>
GenericGrid<T>::GenericGrid(size_t numRows, size_t numColumns, const T & inInitialValue):
 mNumRows(numRows),
 mNumColumns(numColumns)
{
 mData.resize(numRows*numColumns, inInitialValue);
}


template<class T>
const T & GenericGrid<T>::get(size_t rowIdx, size_t colIdx) const
{
 return mData[rowIdx*mNumColumns + colIdx];
}


template<class T>
T & GenericGrid<T>::get(size_t rowIdx, size_t colIdx)
{
 return mData[rowIdx*mNumColumns + colIdx];
}


template<class T>
void GenericGrid<T>::set(size_t rowIdx, size_t colIdx, const T & inT)
{
 mData[rowIdx*mNumColumns + colIdx] = inT;
}


template<class T>
size_t GenericGrid<T>::numRows() const
{
 return mNumRows;
}


template<class T>
size_t GenericGrid<T>::numColumns() const
{
 return mNumColumns;
}
StackedCrooked