Two Dimensional Arrays
Introduction
See full imageThe 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.