Pointers and MultiDimensional Arrays in C/C++

In this user guide, we will learn about pointers and multidimensional arrays. In our previous tutorial, we looked upon the general relationship of arrays with pointers and how to work with them. Follow the link given below to access that article:

We have already looked at pointers in the context of one-dimensional arrays. Now let move ahead and see how we can work with two-dimensional or even three-dimensional arrays using pointers.

Pointers with 1-D Arrays

To understand this concept better let us first briefly look at how to handle one dimensional array with pointers. First, we will declare a one dimensional array for e.g. an integer array num[] of size 5:

int num[5];

This means we are creating five different integer variables that are named num[0], num[1], num[2], num[3] and num[4] as one contiguous block of memory. This can be seen below as a section of the system’s memory.

Pointers and multi-dimensional arrays pic1

The starting address of ‘num’ is ‘100.’ As we know byte in the system’s memory has an address and if we assume that an integer is stored in 4 bytes in a typical compiler then the block of four bytes at starting address 100 will be num[0]. Block of 4 bytes at starting address 104 will be num[1]. Next block of 4 bytes at starting address 108 will be num[2] and so on.

Lets store some values in this array for e.g:

int num[]={10,11,12,13,14};
Pointers and multi-dimensional arrays pic2

Now let us look at the following statement:

int *ptr = num;

If we just use the variable name ‘num’ then the name of the array in an expression basically returns a pointer to the first element of the array. As ‘num’ is an array of integers so each element will also be an integer. Hence, ‘num’ returns a pointer to integer. After writing the above statement, we can now use pointer arithmetic and dereferencing to access all the elements in the array.

If we print the address stored in ‘ptr’ then the output will be 100.

printf("\n%d",ptr);

Dereferencing ‘ptr’ will give us the output ’10’.

printf("\n%d",*ptr);

If we print *(ptr+2), then the output will be 12. This is because we have an integer pointer adding 2. This will take us to the address of the next to next integer which will be 8 bytes ahead.

printf("\n%d",*(ptr+2));

Language gives us this flexibility that we can use the name of the array just like a pointer for all the dereferencing. So instead printing ‘ptr’ we can also do all of this with ‘num’. The output will still be the same.

printf("\n%d",num);
printf("\n%d",*num);
printf("\n%d",*(num+2));

In fact *(num+i) is same as num[i]. These are alternate syntax. Similarly, *(num+i) is same as &A(i). Both will give us the address of the i-th element in the array.

Thing to note: Even though we can use the name of the array just like pointer for all this dereferencing and arithmetic, it does not seem like a pointer variable. We can write the statement ptr= num; However, we can not write num =ptr; This will give us compilation errors.

Pointers with 2-D Arrays

Now let us look at how to handle pointers with two dimensional arrays. For example purposes, we will declare the following two dimensional array of integers named ‘num’.

int num[2][3]

We have created an array of array. ‘2’ is the number of rows and ‘3’ is the number of columns. In fact, we have created two one dimensional arrays consisting of three elements each. This time num[0] and num[1] are both one dimensional arrays of three integers each. A one dimensional array of three integers would be 12 bytes if each integer is 4 bytes in size. So, showing it in the system’s memory the first block of 12 bytes starting address 200 will be num[0] and the next block of 12 bytes starting address 212 will be num[1].

Pointers and multi-dimensional arrays pic3

Point to Note: In the 2-D array, the address of a block is the address of the first byte in the block.

As we know that the name of the array returns a pointer to the first element in the array. This time each element is not an integer but instead each element is a one-dimensional array of three integers. So if we will write a statement like this:

int *ptr=num;

This will give us a compilation error. This is because ‘num’ will return a pointer to one dimensional array of three integers and not just a pointer to integer. The type of a pointer matters not when you have to read the address. It matters when you dereference or when you perform pointer arithmetic. It is very important to understand this.

We can define a pointer to a one-dimensional array of three integers in the following way.

int (*ptr)[3];

Equate ‘ptr’ with num:

int (*ptr)[3]=num;

Now we will print just ‘num’ which is the same as printing the address of num[0] through &num[0]. Thus, the output will be 200. However, if we will print *num which will be same as num[0] then num[0] is the variable name for the one dimensional array of three integers. So just using the name num[0] will return a pointer to the first integer in num[0]. This will be accessed as &num[0][0].

We will store values in our 2-D array as shown in the following statement.

int num[2][3]={{4,5,6},{14,24,34}};

You can view the individual integers in the memory blocks below.

Pointers and multi-dimensional arrays pic4

Here you can see num[0] consists of three integer elements num[0][0] that is 4, num[0][1] that is 5 and num[0]num[2] that is 6. Likewise, num[1] also consists of three elements num[1][0] that is 12,num[1][1] that is 24 and num[1][2] that is 34.

Pointers and multi-dimensional arrays pic5

Pointer Arithmetic and dereferencing with 2-D arrays

  • Now if we print (num+1), the output will be 212. This is because num returns a pointer to one dimensional array of three integers. So if we will perform pointer arithmetic of adding plus 1 we are moving to the next 1-D array of three integers. Hence, we will be moving to address 200+size of 1-D array of 3 integers in bytes so that is why the output will be (200+12)=212. This is true because num+1 is equivalent to &num[1].
  • Let us print *(num+1). Notice that we put an asterisk sign to dereference. Here the type of pointer holds significance. ‘num’ is a pointer to a 1-D array of three integers. Hence, (num+1) is also a pointer to 1-D array of 3 integers. When we will dereference this we will obtain the whole 1-D array of three integers with starting address 212. *(num+1) is the same as num[1]. This 1-D array num[1] which should return us the pointer to the first integer in num[1]. Thus, *(num+1) is basically the pointer to the integer at address 212. In other words, &num[1][0]. Hence the output will be again 212. Notice that all these expressions including *(num+1) or num[1] or &num[1][0] are returning an integer pointer and are equivalent statements.
  • Now let us look at a bit harder one. What will be the output if we print *(num+1)+2?

As we saw above, *(num+1) returned us the integer pointer to the first integer in num[1] to the integer at address 212. Adding plus 2 here is performing pointer arithmetic because *(num+1) is a pointer to integer stored at address 212. Adding 2 means we are moving ahead to the next to next address of the integer thus skipping 8 bytes. We will end up at address (212+8)=220. *(num+1) in this expression can be written as num[1] as they are equivalent statements. Thus *(num+1)+2 is the same as num[1]+2 or &num[1][2]. All of these expressions are returning pointer to integer. In fact they are returning the the pointer corresponding to the element num[1][2].

  • Lastly, let us find out the output if we print *(*num+1)

Whenever we encounter an expression with pointer arithmetic and dereferencing make sure to solve it in a series of steps to make it easier to obtain the output.

  1. Here ‘num’ is returning us the pointer to the 1-D array of size three. Then we are dereferencing this 1-D array of three integers. This will give us the 1-D array of integers so *num will give us num[0].
  2. Now num[0] is named for a 1-D array returns us the pointer to the first integer in the 1-D array so num[0] returns a pointer to integer. We will get a pointer to integer at address 200.
  3. Adding 1 to an integer pointer will take us 4 bytes ahead to the next integer. Thus, it will return us the pointer to the next integer. We will obtain a pointer to this integer at address 204. So we can say that *(num+1) is basically &num[0][1].
  4. With the final dereferencing *(&num[0][1]), we can get rid of the & operator so the overall expression now becomes num[0][1]. This is equal to ‘5’ in our case.

Note: For a 2-D array, num[i][j] where num is the name of our 2-D array i and j are indices, can be written as:

num[i][j] = *(num[i]+j)

= *(*(num+i)+j) where num[i] can be re-written as *(num+i)

Hence these three expressions are equivalent.

Pointers with 3-D Arrays

Now let us look at how to handle pointers with three dimensional arrays. For example purposes, we will declare the following three dimensional array of integers named ‘num’.

int num[3][2][2]

We have an array of 3x2x2. A three dimensional array is an array or collection of 2-D arrays. Here the first parameter ‘3’ is the block size or the number of 2-D arrays, the next parameter ‘2’ is the number of rows of the 2-D arrays and last parameter ‘2’ is the number of columns of the 2-D arrays. The following figure depicts ‘num’ in the system’s memory.

Pointers and multi-dimensional arrays pic6

Here we have assumed that the starting address of ‘num’ is 400. The first sixteen bytes at starting address 400 is the first 2-D array which is num[0]. We are assuming that each integer will take 4 bytes so the first block is part of the first 2-D array. The next block of sixteen bytes with starting address 416 is num[1]. Likewise ,the next block of sixteen bytes with starting address 432 is num[2].

We will store values in our 3-D array as shown in the following statement.

int num[3][2][2]=
         { {{4,14},{20,31}},    //elements of block 1
           {{30,6},{36,32}},    //elements of block 2
           {{9,0},{19,67}}     //elements of block 3
         };

You can view the individual integers in the memory blocks below.

Pointers and multi-dimensional arrays pic7

We can further break down the 2-D arrays into 1-D arrays. The first two integers in num[0] are part of the first 1-D array num[0][0]. The next two integers 20 and 31 in our case are part of num[0][1] and as follows as shown in the figure below:

Pointers and multi-dimensional arrays pic8

The first integer in num[0][0] can be accessed as num[0][0][0]. We can access each integer in the following manner as shown below:

Pointers and multi-dimensional arrays pic9

This time just using the array name ‘num’ will give us a pointer to a 2-D array of integers of size 2×2.

Pointer Arithmetic and dereferencing with 3-D arrays

The following statement declares a pointer to 2-D array of integers of size 2×2:

int (*ptr)[2][2]= num;
  • The name of the pointer is ‘ptr’. Printing ‘prt’ or ‘num’ gives the output 400.
  • Now if we will perform dereferencing and try to print *num then this will be the same as printing num[0]. As num[0] is a 2-D array so we will get a pointer the first element in num[0] which is accessed through &num[0][0]. All of these expressions return pointer to one dimensional array of integers of size 2. The address printed will be 800. Remember ‘num’ is a type pointer to 2-D array of 2×2 and dereferencing once gives us pointer to 1-D array of 2 integers. ‘num’ is returning us pointer to 2-D array of integers.

For ‘num’, num[i][j][k], where i, j and k are some indices, can be written as *(num[i][j]+k). We can write num[i][j] as *(num[i]+j) and num[i] as *(num+i) so the overall expression becomes:

num[i][j][k] = *(num[i][j]+k)

= *(*(num[i]+j)+k)

= *(*(*(num+i)+j)+k)

  • Let us print *(num[0][1]+1) and see what the output will be. num[0][1] means we are going to the 1-D array that has two elements ’20’ and ’31’ in our case. Using the name num[0][1] means that we are obtaining the pointer to the first integer in this 1-D array. Now pointer to this integer ’20’ plus 1 means that we are moving ahead one integer that is ’31.’ Dereferencing this gives the output as ’31.’

Have a look at the section of the system’s memory to have a better understanding of the scenario:

Pointers and multi-dimensional arrays pic8

Now let us find the output if we print *(num[1]+1). Here num[1] will return us a pointer to the 1-D array. The 1-D array in num[1] is the block containing the integers ’30’ and ‘6’ basically num[1][0]. Adding 1 to this is performing pointer arithmetic and thus moving ahead to the next 1-D array. This is the 1-D array which contains the integers ’36’ and ’32’ basically num[1][1]. Then we will dereference it to get the 1-D array from the pointer. As we used the name of the 1-D array we will get the pointer to the first element in the 1-D array. So we will get the pointer to the integer ’36’. Hence the output will be 424.

Have a look at the section of the system’s memory to have a better understanding of the scenario:

Pointers and multi-dimensional arrays pic10

Example Code

Now let’s look at sample example codes for all the different concepts we have learned so far.

#include <stdio.h>

int main()
{
   int num[3][2][2]= { {{4,14},{20,31}},   
                       {{30,6},{36,32}},  
                       {{9,0},{19,67}}     
                     };
    printf("%d %d %d %d",num,*num,num[0],&num[0][0]) ;    
 
}

Now let’s see the code output. After the compilation of the above code, you will get the following output.

Pointers and multi-dimensional arrays output pic1

As you may notice that all four expressions gave us the same output as was expected. All of these expressions return a pointer to a one-dimensional array of integers of size 2. They are equivalent statements.

Now let’s print the following statement:

 printf("%d",*(num[0][0]+1)) ;    

After the compilation, you will get the following output.

Pointers and multi-dimensional arrays output pic2

As this expression is equivalent to num[0][0][1] hence we will obtain the output ’14’.

You may also like to read other tutorials on pointers:

  1. Pointer Arithmetic, Pointer Size, and Pointer Type in C and C++
  2. C Program to change Endianness of data and bytes swapping example using Pointers
  3. Function Pointers in C/C++ and Function Callbacks
  4. Pointers as Function Arguments or call by reference in C
  5. Double Pointers or Pointers to Pointers in C/C++
  6. How to find the Size of structure without sizeof() Operator?
  7. Dynamic Memory Allocation through Double Pointer and Function without returning address

Leave a Comment