You are here:

C++/creating 2D array dynamically

Advertisement


Question
Hello, Ralph!

Is it possible to have a dynamic 2D array in C++? I am going to apply this in the computation of a statistics-related program in which one of the inputs (which is actually from a text file) is the dimension of the table. That means my program should create a table (a 2D array) depending on the dimension stated in the text file.

Thank you very much and more power!

Answer
In short no; you can only create scalar objects and vectors (one dimensional arrays) of objects dynamically.

However you can get a similar effect by doing the number-of-elements and subscript-to-element calculations by hand thus:

int main()
{
   size_t number_of_rows(0);
   size_t number_of_cols(0);

   // Obtain values for number_of_rows and number_of_cols
   // E.g. from a file or from the user.
   // For this simple example I shall just set them to fixed values
   number_of_rows = 10;
   number_of_cols = 20;

   // Obtain enough space for all elements in the array.
   // As a 2D array is rectangular this is the product of
   // number_of_rows and number_of_cols.
   // Memory for the elements is one contiguous section of memory.
   int * two_d_array_of_int( new int[number_of_rows*number_of_cols] );

   // Clear all elements in the array as if it were a 2D array using
   // the 2D element-from-subscripts formula: number_of_cols*row + col
   for ( size_t row(0); row != number_of_rows; ++row )
   {
       for ( size_t col(0); col != number_of_cols; ++col )
       {
         two_d_array_of_int[number_of_cols*row + col] = 0;
       }
   }

   // Remember to delete the dynamically obtained objects using
   // the array delete operator when done
   delete [] two_d_array_of_int;
}

The calculations used above are pretty much those used by C and C++ for build in non-dynamic 2D arrays, thus:

  int two_d_array_of_int[10][20];  // [rows][cols]

It implies that in C and C++ the built in arrays store the elements with the location of the elements given by the right most (i.e. columns subscript) being closer to each other.

The syntax of the multi-dimensional arrays in C and C++ in fact means that a 2D array is an array whose elements are themselves arrays.

Note that the above solution wastes no more storage than a built in 2D array and like a built in 2D array stores the elements in one contiguous section of memory. The downside of course is that the access method using the explicit calculation is not as clear as the built in array access syntax.

We can instead use an array of pointers to arrays. In this solution we first allocate a vector (1D array) of pointers to the objects we wish (ints in my examples), one for each row, and then allocate additional vectors of the size of the number of columns required and assign the pointers to these column vectors to each of the row vector pointer elements, thus:

int main()
{
   size_t number_of_rows(0);
   size_t number_of_cols(0);

   // Obtain values for number_of_rows and number_of_cols
   // E.g. from a file or from the user.
   // For this simple example I shall just set them to fixed values
   number_of_rows = 10;
   number_of_cols = 20;

   // Obtain enough space for all elements in the array.
   // We do this in two stages:
   // First allocate and create number_of_rows pointers to int.
   // Secondly for each int pointer in the array allocate and create
   // number_of_cols ints.
   // Storage for the elements is in several sections of memory:
   // One section for the row pointers to int.
   // One section for _each_ row of columns of int.
   int ** two_d_array_of_int( new int*[number_of_rows] );
   for ( size_t row(0); row != number_of_rows; ++row )
   {
       two_d_array_of_int[row] = new int[number_of_cols];
   }

   // Clear all elements in the array as if it were a 2D array
   // using C/C++ built in 2D array access syntax
   for ( size_t row(0); row != number_of_rows; ++row )
   {
       for ( size_t col(0); col != number_of_cols; ++col )
       {
         two_d_array_of_int[row][col] = 0;
       }
   }

   // Remember to delete the dynamically obtained objects using
   // the array delete operator when done, in the reverse order
   // the memory was obtained:
   // First the column arrays pointed to by the int pointers in the
   // two_d_array_of_int.
   for ( size_t row(0); row != number_of_rows; ++row )
   {
       delete [] two_d_array_of_int[row];
   }

   // Second the array of pointers to in themselves
   delete [] two_d_array_of_int;
}

In this solution we can use the familiar array[row][col] syntax to access the elements of the array, and in fact separating the rows and columns like this allow for the columns to be different sizes. The down sides are that creation and destruction are more complex, more space is required to store the pointers to the columns, and the memory is no longer in a single contiguous section (which may or may not be important).

Some of the downsides of these solutions can be largely be hidden by wrapping the details in a class and providing access member functions that take the row and column number and return the element value. The details of how the storage is managed are then largely an implementation detail, maybe separate classes could be created for each of the ways I have discussed.

Unfortunately we cannot write an operator[] function that takes more than one subscript argument as this is not allowed, so we cannot write code that would allow us to do something like:

   Array2D table(10,20);
   table[5,5] = 0;

In general nor would using the usual syntax such as:

   table[5][5] = 0;

As the operator[] for our Array2D class would return a reference to the contents of the element at row subscript 5 (the 6th row as C/C++ subscripts are zero based) and you cannot apply the [] operator to a reference. However for the 2D case only we can return a pointer to the start of the column for the given row - working much like the vector of row pointers solution, thus:

    table[5]

would return to a pointer to int that is the start of the column elements for row 5. We can then apply the [] operator to this pointer so that:

   table[5][5]

Refers to the int at element 5 of the column for row 5.

Note that for arrays with higher dimensions this simple technique does not work (although it could be make to work with other more involved techniques) as the compiler would require knowledge of the dimensions of the intermediate arrays. For example for a 3D array (of a class type Array3D maybe) consider the equivalent request to its operator[] to that for the Array2D case shown previously:

   table3d[5]

In this case the result would have to be a 2D array (or a pointer to a 2D array), and this would mean that we would require information on the geometry of this array at compile time to allow the compiler to perform its static built in array subscript-to-element calculations. As previously we would require the number of columns for the returned 2D array.

It works for the 2D case because we can return a vector (or a pointer to a vector) and the compiler knows that elements are contiguous in a vector. It knows that element n+1 is one location on from element n where one location is taken to be the size of the type of the elements (so is 1 for char, but might be 4 for an int).

Below is a very basic (and incomplete) example implementation of such an Array2D class that uses the first method to store the data. Only just enough has been implemented to demonstrate the above points and more work would be required to make it a generally useable class:

   class Array2D
   {
   public:
       Array2D( size_t rows, size_t cols )
       : iData( new int[rows*cols] )
       , iRows( rows )
       , iCols( cols )
       {
       }

     // Add copy and assignment etc.

     ~Array2D()
     {
         delete [] iData;
     }

     int * operator[]( size_t row )
     {
         return iData+iCols*row;
     }

   private:
       int *   iData;
       size_t  iRows;
       size_t  iCols;
   };

As can be seen the class has a constructor that takes the row and column bounds and creates a vector containing enough storage for all the elements. The bounds are also stored, although for the partial implementation shown here we only really require number of column value. The row bound value could be used to validate row element values for example. The destructor deletes the array created in the constructor.

The subscript operator (i.e. operator[]) takes only the row subscript. It uses this to calculate and return the pointer value of the start of the column for the specified row.

We can use the Array2D class like so:

int main()
{
   size_t number_of_rows(0);
   size_t number_of_cols(0);

   number_of_rows = 10;
   number_of_cols = 20;

   Array2D table(number_of_rows,number_of_cols);

   for ( size_t row(0); row != number_of_rows; ++row )
   {
       for ( size_t col(0); col != number_of_cols; ++col )
       {
         table[row][col] = row*col;
       }
   }

   std::cout << "Table[5][5] = " << table[5][5] << '\n';
}

The alternative to using operator[] is to use a named member function such as at() following on from the std::vector usage, or maybe another operator such as operator().

Before you rush off to develop your own array class(es) take a look at some existing multi dimension array implementations such as the Boost (http://www.boost.org/) MultiArray library at http://www.boost.org/libs/multi_array/doc/index.html.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.