Pointer-To-Pointer Dynamic Two-Dimensional Array

Pointer-to-pointer dynamic two-dimensional array

The first method cannot be used to create dynamic 2D arrays because by doing:

int *board[4];

you essentially allocated an array of 4 pointers to int on stack. Therefore, if you now populate each of these 4 pointers with a dynamic array:

for (int i = 0; i < 4; ++i) {
board[i] = new int[10];
}

what you end-up with is a 2D array with static number of rows (in this case 4) and dynamic number of columns (in this case 10). So it is not fully dynamic because when you allocate an array on stack you should specify a constant size, i.e. known at compile-time. Dynamic array is called dynamic because its size is not necessary to be known at compile-time, but can rather be determined by some variable in runtime.

Once again, when you do:

int *board[4];

or:

const int x = 4; // <--- `const` qualifier is absolutely needed in this case!
int *board[x];

you supply a constant known at compile-time (in this case 4 or x) so that compiler can now pre-allocate this memory for your array, and when your program is loaded into the memory it would already have this amount of memory for the board array, that's why it is called static, i.e. because the size is hard-coded and cannot be changed dynamically (in runtime).

On the other hand, when you do:

int **board;
board = new int*[10];

or:

int x = 10; // <--- Notice that it does not have to be `const` anymore!
int **board;
board = new int*[x];

the compiler does not know how much memory board array will require, and therefore it does not pre-allocate anything. But when you start your program, the size of array would be determined by the value of x variable (in runtime) and the corresponding space for board array would be allocated on so-called heap - the area of memory where all programs running on your computer can allocate unknown beforehand (at compile-time) amounts memory for personal usage.

As a result, to truly create dynamic 2D array you have to go with the second method:

int **board;
board = new int*[10]; // dynamic array (size 10) of pointers to int

for (int i = 0; i < 10; ++i) {
board[i] = new int[10];
// each i-th pointer is now pointing to dynamic array (size 10) of actual int values
}

We've just created an square 2D array with 10 by 10 dimensions. To traverse it and populate it with actual values, for example 1, we could use nested loops:

for (int i = 0; i < 10; ++i) {   // for each row
for (int j = 0; j < 10; ++j) { // for each column
board[i][j] = 1;
}
}

Pointer to a 2D dynamic Array Pointer

Further down the answer, you find more possible solutions.

First, I want to present a solution to you, where you manage a dynamic 2d array with a 1d pointer, in case of same-length columns.

#include <stdlib.h>
#include <stdio.h>

struct XYZ
{
int someValue;
};

struct XYZ* GetArrayItem(struct XYZ* itemArray, int numCols, int row, int col)
{
return &itemArray[row * numCols + col];
}

int main()
{
int A = 5;
int B = 4;
struct XYZ* arr = (struct XYZ*)calloc(A*B, sizeof(struct XYZ));

for (int i = 0; i < A; i++)
{
for (int j = 0; j < B; j++)
{
GetArrayItem(arr, B, i, j)->someValue = 1;
}
}

free(arr);

return 0;
}

With columns of different length, a double pointer might be a viable solution.

struct XYZ
{
int someValue;
};

int main()
{
int i;
int j;
// row count
int A = 5;
// column count per row
int B[] = { 3, 4, 3, 2, 4 };
struct XYZ** arr = (struct XYZ**)calloc(A, sizeof(struct XYZ*));
for (i = 0; i < A; i++)
{
// initialize column for each row
arr[i] = (struct XYZ*)calloc(B[i], sizeof(struct XYZ));
}

for (i = 0; i < A; i++)
{
for (j = 0; j < B[i]; j++)
{
// access items
arr[i][j].someValue = 1;
}
}

for (i = 0; i < A; i++)
{
free(arr[i]);
}

free(arr);

return 0;
}

However, I would advise you to create a more explicit object structure in case where you need 2d data. This makes the design more explicit and the column count per row more transparent.

struct XYZ
{
int someValue;
};

struct MyDomainSpecificRow
{
int numColumns;
struct XYZ* myRowData;
};

int main()
{
int i;
int j;
// row count
int A = 5;
// column count per row
int B[] = { 3, 4, 3, 2, 4 };
// 1d array of rows, each containing 1d array of cells
struct MyDomainSpecificRow* arr = (struct MyDomainSpecificRow*)calloc(A, sizeof(struct MyDomainSpecificRow));

for (i = 0; i < A; i++)
{
// initialize column for each row
arr[i].numColumns = B[i];
arr[i].myRowData = (struct XYZ*)calloc(B[i], sizeof(struct XYZ));
}

for (i = 0; i < A; i++)
{
for (j = 0; j < arr[i].numColumns; j++)
{
// access items
arr[i].myRowData[j].someValue = 1;
}
}

for (i = 0; i < A; i++)
{
free(arr[i].myRowData);
}

free(arr);

return 0;
}

Passing Dynamic Two Dimensional Array as argument to a functoin in c++

you need to get the parameter in your function as pointer

void displayArray(int **a)
{
for (int i=0; i<10; i++)
{
for(int j=0; j<10; j++)
{
cout<< a[i][j] <<"\t";
}
cout<<endl;
}
}

int main()
{
int** a = new int*[10];
for(int i = 0; i < 10; ++i)
a[i] = new int[10];

displayArray(a);
}

it prints 10 rows and columns of value 0 because the 2D array is uninitialized

Two-dimensional dynamic array pointer access

I'll assume m is the number of columns, and n the number of rows (you can use n instead of m in my answer if it's the opposite).

In order to access the 2D array, you need 2 indices - let's call them x and y:

x index will be in the range 0 .. m-1, and

y index will be in the range 0 .. n-1

You can calculate the index for your p array in the following way:

int p_idx = y * m + x

Then you can access your arrays element e.g. this way:

p[p_idx] = 111;   // set an element value
int a = p[p_idx]; // get an element value

How C/C++ compiler distinguish regular two dimensional array and array of pointers to arrays?

For this declaration of an array

int a1[N][M] = { {0,1,2}, {3,4,5}, {6,7,8} };

these records

int x = a1[1][2];
int y = *(a1+2+N*1);

are not equivalent.

The second one is incorrect. The expression *(a1+2+N*1) has the type int[3] that is implicitly converted to an object of the type int * used as an initializer. So the integer variable y is initialized by a pointer.

The operator a1[1] is evaluated like *( a1 + 1 ) . The result is a one-dimensional array of the type int[3].

So applying the second subscript operator you will get *( *( a1 + 1 ) + 2 ).

The difference between the expressions when used the two-dimensional array and the dynamically allocated array is that the designator of the two-dimensional array in this expression (a1 + 1) is implicitly converted to a pointer to its first element of the type int ( * )[3] while the pointer to the dynamically allocated array of pointers still have the same type int **.

In the first case dereferencing the expression *(a1 + 1 ) you will get lvalue of the type int[3] that in turn used in the expression *( a1 + 1) + 2 is again implicitly converted to a pointer of the type int *.

In the second case the expression *(a1 + 1) yields an object of the type int *.

In the both cases there is used the pointer arithmetic. The difference is that when you are using arrays in the subscript operator then they are implicitly converted to pointers to their first elements.

When you are allocating dynamically arrays when you are already deals with pointers to their first elements.

For example instead of these allocations

int** a2 = new int*[N];
for (int i = 0; i < N; i++)
a2[i] = new int[M];

you could just write

int ( *a2 )[M] = new int[N][M];

Dynamic allocation/deallocation of array of pointers

One way is to use a 1D contiguous array and implement the 2D index to 1D index mapping instead:

#include <iostream>
#include <cassert>
using namespace std;

template<typename DataType, unsigned numRows, unsigned numCols>
class Container2D {
public:
Container2D() { m_data = new DataType[numRows * numCols]; }
~Container2D() { delete [] m_data; }
inline DataType operator()(unsigned i, unsigned j) const {
assert( 0 <= i && i < numRows);
assert( 0 <= j && j < numCols);
return m_data[i*numCols+j];
}
inline DataType& operator()(unsigned i, unsigned j) {
// same as above, but this allows inplace modifications
assert( 0 <= i && i < numRows);
assert( 0 <= j && j < numCols);
return m_data[i*numCols+j];
}
private:
DataType* m_data;
};

int main() {

Container2D<int, 3, 3> container;
int x = container(0,0); // retrieve the element at (0,0);
container(1,2) = 9; // modify the element at (1,2);
// int y = container(3,0); // triggers assertion errors for out-of-bound indexing

return 0;
}

Notes:

  • If numRows and numCols do not change for a specific class instance, new and delete are not necessary in this case. If they do dynamically change, it is better to store them as member variables instead of template parameters. If numRows and numCols are too large, one can dynamically allocate Container2D objects as a whole.
  • As @GoswinvonBrederlow commented, there is no difference between this and new m_data[numRows][numCols] in terms of memory layout, but this convention makes it easier to extend to higher dimensions.


Related Topics



Leave a reply



Submit