Two Dimensional Arrays
Introduction
See full image
The above picture shows a double pointer being used as a 2 dimensional
array. We have a double pointer that can be declared as:
int** DoublePointer ;
Now if we do a *DoublePointer we will end up at the beginning of the
block with address 100 and a value inside it. In this case we have a
block so "*(DoublePointer+1)" will lead us to the element of 101. We
can also use array notation and get at the values using :
DoublePointer[0] and DobulePointer[1]
These are the rows of our 2 dimensional array. Now each pointer points
to a block of memory and if we follow the "DoublePointer[0]" and say
*(DoublePointer[0])
That will give us the first element of the block in RAM . We can
also write the above as:
DoublePointer[0][0]
This is how we can construct a 2 dimensional array dynamically. Ex:
File: two1.cpp
#include <iostream> using namespace std ; int main() { int** TwoDimArray ; const int ROW_COUNT = 2 ; const int COL_COUNT = 3 ; TwoDimArray = new int*[ROW_COUNT] ; for(int i1=0 , k1=1 ; i1<ROW_COUNT ; i1++ ) { TwoDimArray[i1] = new int[COL_COUNT] ; for( int j1=0 ; j1<COL_COUNT ; j1++ ) { TwoDimArray[i1][j1] = k1++ ; } } //for //print contents using array notation for(int i1=0 ; i1<ROW_COUNT ; i1++ ) { for( int j1=0 ; j1<COL_COUNT ; j1++ ) { cout << TwoDimArray[i1][j1] << " " ; } cout << endl ; } //for cout << endl << endl ; //print contents using pointer notation for(int i1=0 ; i1<ROW_COUNT ; i1++ ) { for( int j1=0 ; j1<COL_COUNT ; j1++ ) { cout << ( *( (*(TwoDimArray+i1)) + j1 ) ) << " " ; } cout << endl ; } //for cout << endl << endl ; for(int i1=0 ; i1<ROW_COUNT ; i1++ ) { delete[] TwoDimArray[i1] ; } delete[] TwoDimArray ; return 0 ; } Output: 1 2 3 4 5 6 1 2 3 4 5 6Constructing a 2 dimensional array using the C notation:
File: two2.cpp
#include <iostream> using namespace std ; int main() { int** TwoDimArray ; const int ROW_COUNT = 2 ; const int COL_COUNT = 3 ; TwoDimArray = (int**)malloc(sizeof(int*)*ROW_COUNT) ; for(int i1=0 , k1=1 ; i1<ROW_COUNT ; i1++ ) { TwoDimArray[i1] = (int*) malloc(sizeof(int)*COL_COUNT) ; for( int j1=0 ; j1<COL_COUNT ; j1++ ) { TwoDimArray[i1][j1] = k1++ ; } } //for //print contents using array notation for(int i1=0 ; i1<ROW_COUNT ; i1++ ) { for( int j1=0 ; j1<COL_COUNT ; j1++ ) { cout << TwoDimArray[i1][j1] << " " ; } cout << endl ; } //for cout << endl << endl ; //print contents using pointer notation for(int i1=0 ; i1<ROW_COUNT ; i1++ ) { for( int j1=0 ; j1<COL_COUNT ; j1++ ) { cout << ( *( (*(TwoDimArray+i1)) + j1 ) ) << " " ; } cout << endl ; } //for cout << endl << endl ; for(int i1=0 ; i1<ROW_COUNT ; i1++ ) { free( TwoDimArray[i1] ) ; } free( TwoDimArray ) ; } Output: 1 2 3 4 5 6Exercises
1) What's wrong with the following program:
Passing arrays and pointers to functions
File: ptr2.cpp
#include <iostream> using namespace std ; void function1( int arr2[] ) { arr2[0] = 100 ; } void printArray( int arr1[] , int size ) { for( int i1=0 ; i1<size ; i1++ ) { cout << arr1[i1] << " " ; } cout << endl ; } int main() { int arr1[] = { 10, 20, 30 } ; function1( arr1 ) ; printArray( arr1, 3 ) ; return 0 ; }Array and pointers are almost the same in that they both contain an address. If we pass an array as an argument to a function and the function changes something in the array then that change is reflected in the calling function. Same behavior is exhibited when we pass a pointer to a function and the function manipulates the value the pointer points to.
File: ptr3.cpp
#include <iostream> using namespace std ; void function1( int arr1[] ) { cout << "function1:" << arr1[0] << endl ; } void function2( int* arr1 ) { cout << "function2:" << arr1[0] << endl ; } void function3( int arr1[][3] ) { cout << "function3:" << arr1[0][0] << endl ; } void function4( int** arr1 ) { cout << "function4:" << arr1[0][0] << endl ; } int main() { //Single Dimensional Arrays int array1[10] = { 1,2,3,4,5,6,7,8,9,10} ; function1( array1 ) ; //Ok to pass an array to a pointer function2( array1 ) ; int* ptr1 = new int[10] ; ptr1[0] = 1 ; ptr1[1] = 2 ; //Ok to pass a pointer to an array function1( ptr1 ) ; function2( ptr1 ) ; //Two Dimensional Arrays int array2[2][3] = { {1,2,3} , {4,5,6} } ; int** TwoDimArray ; const int ROW_COUNT = 2 ; const int COL_COUNT = 3 ; TwoDimArray = (int**)malloc(sizeof(int*)*ROW_COUNT) ; for(int i1=0 , k1=1 ; i1<ROW_COUNT ; i1++ ) { TwoDimArray[i1] = (int*) malloc(sizeof(int)*COL_COUNT) ; for( int j1=0 ; j1<COL_COUNT ; j1++ ) { TwoDimArray[i1][j1] = k1++ ; } } //for function3( array2 ) ; // Compiler error // function4( array2 ) ; // Compiler error //function3( TwoDimArray ) ; function4( TwoDimArray ) ; return 0 ; } Output: function1:1 function2:1 function1:1 function2:1 function3:1 function4:1
The above code illustrates how single and two dimensional
arrays can be passed to functions. With a single dimensional
array we can pass an array variable to a function taking
an array argument or a pointer argument. After all arrays
and pointers hold addresses and we should be able to use
either one.
The declarations for "functions1" and "function2" are:
void function1( int arr1[] )
void function2( int* arr1 )
We are passing the below to both the functions:
int array1[10] = { 1,2,3,4,5,6,7,8,9,10} ;
function1( array1 ) ;
//Ok to pass an array to a pointer
function2( array1 ) ;
int* ptr1 = new int[10] ;
ptr1[0] = 1 ; ptr1[1] = 2 ;
//Ok to pass a pointer to an array
function1( ptr1 ) ;
function2( ptr1 ) ;
In the first case we have an array variable and we can
pass it to both the functions and in the second case we
have a pointer that we can pass to both functions .
The code compiles and the first element in the block
is printed in both cases.
Now we have the 2 dimensional array case:
void function3( int arr1[][3] )
{
cout << "function3:" << arr1[0][0] << endl ;
}
void function4( int** arr1 )
{
cout << "function4:" << arr1[0][0] << endl ;
}
We can see that the notation to access the 2 dimensional
arrays is the same "arr1[0][0]" . So we expect the
functions to take either a 2 dimensional array or a
double pointer. But that is not the case.
function3( array2 ) ;
// Compiler error
// function4( array2 ) ;
// Compiler error
//function3( TwoDimArray ) ;
function4( TwoDimArray ) ;
If we try to pass a 2 dimensional array to a double pointer
then we receive a compiler error and if we try to pass a
double pointer to a 2 dimensional array then also we receive
a compiler error. Why does the compiler not allow the
conversion. To understand this we need to understand how
the 2 types are stored in RAM.
int array2[2][3] = { {1,2,3} , {4,5,6} } ;
int** TwoDimArray ;
RAM
array2 0 100
100 1
2
3
4
6
RAM
TwoDimArray 0 100
100 Address of first row
200 Address of second row
If we have a 2 dimensional array then we the elements are
stored in a linear fashion. We must know the number of
columns in order to find out when the rows start. However
with a double pointer we store a block of addresses and
not the actual elements .
We follow the address to the block and then grab the
elements from the block. If
function4( array2 ) ;
did compile then things will not work as expected.
Remember "array2" is actually a 2 dimensional array.
Then function4 converts it to a double pointer and then we
do arr1[0][0] .
Now the first "arr1[0]" gives us the element "1" but the
argument was a double pointer. Now the compiler is going
to go to the block with the memory address of "1" .
However "1" is the actual element and not an address.
We cannot convert between a legitimate 2 dimensional array
and a double pointer. Compiler is going to get confused
because they store things in a different fashion in the RAM.