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, andy
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
andnumCols
do not change for a specific class instance,new
anddelete
are not necessary in this case. If they do dynamically change, it is better to store them as member variables instead of template parameters. IfnumRows
andnumCols
are too large, one can dynamically allocateContainer2D
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
Most Optimized Way of Concatenation in Strings
Fast Fixed Point Pow, Log, Exp and Sqrt
Capturing Perfectly-Forwarded Variable in Lambda
Sse, Intrinsics, and Alignment
All Combinations of K Elements Out of N
Function Prologue and Epilogue in C
Template Specialization and Enable_If Problems
Rationale of Enforcing Some Operators to Be Members
How to Use C++ with Objective-C in Xcode
C++/C Function Pointers That Return Void*
Why Do We Actually Need Private or Protected Inheritance in C++
When Do I Really Need to Use Atomic<Bool> Instead of Bool
How to Safely Use Openmp with C++11
Will Casting Around Sockaddr_Storage and Sockaddr_In Break Strict Aliasing
C/C++ Changing the Value of a Const
How to Use Covariant Return Types with Smart Pointers