11

There are several ways to define a 2D array in C++ and STL without memory manipulation, and the following codes illustrate two different methods:

int main () 
{
    /**************
        1   2   3
        4   5   6
    ***************/
    // Method 1
    const int ROW = 2;
    const int COL = 3;
    int array1[ROW][COL];
    for(int i=0; i<ROW; i++)
        for(int j=0; j<COL; j++)
            array1[i][j] = i*COL+j+1;

    // Method 2
    typedef vector<vector<int> > ARRAY; 
    ARRAY array2;
    vector<int> rowvector;
    for(int i=0; i<ROW; i++)
    {
        rowvector.clear();
        for(int j=0; j<COL; j++)
            rowvector.push_back(i*COL+j+1);
        array2.push_back(rowvector);
    }
    return 0;
}

My question is: are there other ways to define the 2D array? Which one is the most efficient one? Thanks!

PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
feelfree
  • 11,175
  • 20
  • 96
  • 167
  • 3
    [Good tutorial](http://stackoverflow.com/questions/4810664/how-do-i-use-arrays-in-c/4810668) about arrays in C++ – PaperBirdMaster Oct 11 '12 at 13:55
  • 1
    If your array is of constant size use method 1. If your array has a size determined at runtime use method 2. Further, less usefull methods for 2d arrays are `std::array, ROW>` and [boost multidimensional arrays](http://www.boost.org/doc/libs/1_51_0/libs/multi_array/doc/user.html). –  Oct 11 '12 at 13:59

8 Answers8

22

In C++11 use std::array:

  std::array<std::array<int,3>,2> a {{
    {{1,2,3}},
    {{4,5,6}}
 }};

Some usage:

  a[0][2] = 13;
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
6

There are a lot of trade-offs here.

If you declare a C-style 2D array int array[height][width], then you really get a single contiguous block of memory. The compiler converts indexes to their 1D address

array[row][col] == *(array + row * width + col)
  • Advantages: cache coherency. All the memory is in the same place.
  • Disadvantages: you need a multiply for every indexing. Indirection might be faster.

If you use a vector of vectors, then each row is allocated separately. The outer vector stores pointers to the inner vectors. Indexing becomes an indirection followed by an addition:

array[row][col] == *(*(array + row) + col)
  • Advantages: indirection may be faster than multiplication.
  • Disadvantages: not cache coherent, since each row is allocated separately (unless the implementation optimizes for vector<vector>).

If performance is truly important, you need to test both and figure out which is faster on your data.

japreiss
  • 11,111
  • 2
  • 40
  • 77
6

A common pattern is encapsulating the 2D array inside a class that offers the appropriate interface. In that case, you can use other internal representations, like for example a single vector of rows*cols elements. The interface (usually operator()(int,int) will map the coordinates from the caller to a position in the linear vector.

The advantage is that it has dynamic allocation, but a single allocation (unlike the std::vector<std::vector<int>> where each vector must acquire it's own memory) and in a single block providing locality of data.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
6

One very efficient method to define arrays is dynamic allocation, using the new and delete operators. Here is an example:

int **arr=new int*[ROW];
for( int i=0; i<ROW; ++i ) {
  arr[i] = new int[COL];
  for( int j=0; j<COL; ++j ) {
    arr[i][j] = some_val;
  }
}

The big advantage of this approach is that when you don't need any more the memory that the array uses, you can easily delete it. Here is an example of deleting a 2D array:

for( int i=0; i<ROW; ++i ) {
  delete[] arr[i];
}
delete[] arr;   
Rontogiannis Aristofanis
  • 8,883
  • 8
  • 41
  • 58
  • 1
    How/When is this efficient? All rows of the array as well as the location of the rows are probably in unrelated memory areas and definitely non contiguous which can be good or bad in regards to cache locality and can cause problems such as if you try to pass these data to functions expecting a continuous memory layout (eg MPI function calls). The question was "how to allocate WITHOUT memory manipulation" and it seems to me that your answer does exactly the opposite. – pattakosn Mar 04 '21 at 13:37
  • 1
    Do not do this. This is the contrary of efficient! You are trashing your cache by creating unneeded indirections and let’s not speak about memory fragmentation – Alejandro Garcia Sep 12 '21 at 01:09
4

are there other ways to define the 2D array?

No without manipulating memory explicitely (malloc/free). If you use static allocated array (1st example) you allocate the space at compile time, so you can't add more rows or columns at runtime.

The second example uses std::vector that hides to you dynamic memory allocation . This way you can eventually add more rows or columns at runtime.

If you don't need to dynamically modify the array dimension, then the first solution is the simpler and faster one (even if I think that std::vector implementation is fast enough to be comparable to static array, more elegant and more object oriented).

If you need to modify the array dimension at run-time use std::vector, because it saves you from dealing directly with malloc and free.

Heisenbug
  • 38,762
  • 28
  • 132
  • 190
  • so either one should know the size in compile time and use `std::array` or choose between `std::vector` and `new`/`malloc`. If only there was an size-immutable `std:array` that did not need to know the size in advance that could be used for many applications like images I get over network that I cannot know the size but when the size is known I do not need the extra complexity of `std::vector` – dashesy Sep 14 '15 at 18:01
4

To declare a 2D array using std::vector, you can use this kind of construction:

vector<vector<int> >  matrix( n, vector<int>(m, -1) );

This creates a 2D array matrix of size n by m, with all elements initialised to -1.

It's basically a nesting of the "initialise with n items of value val" constructor:

vector (size_type n, const value_type& val,
                 const allocator_type& alloc = allocator_type());

(constructor definition copied from here)

MatthewD
  • 2,509
  • 2
  • 23
  • 27
  • Since m is alphabetically before n, it might cause confusion, though you specified. In other words, in order to use a 2D array like this: c[x][y], you don't swap the positions of x and y: vector> c( x, vector(y, 0)); – ClarHandsome Mar 28 '19 at 05:14
0

If you know the elements beforehand then you could just do

int arr[2][3] = {{1,2, 3}, {4, 5, 6}};

This should be more efficient than method1 and method2. Using vectors, you are not doing memory manipulation yourself, but the vector implementation will probably use a dynamically allocated array.

ctd
  • 1,693
  • 12
  • 27
kkg
  • 69
  • 5
-1

you can something like this vector> m_2DArray;

then once you know the number of rows (rows) and number of columns (columns), you can resize your 2d array

m_2DArray.resize(rows);

for(auto& el:m_2DArray) el.resize(columns);

you can access data in the 2d array using m_2DArray[i][j], just like any other 2D array

YounesCHTIOUI
  • 21
  • 1
  • 7