Wednesday, November 2, 2011

Pointers and Arrays in C programming language

To be able to see what pointers have got to do with arrays, let us first learn some pointer arithmetic. Consider the following
example:

main( )
{
      int  i = 3, *x ;
      float  j = 1.5, *y ;
      char  k = 'c', *z ;

      printf ( "\nValue of i = %d", i ) ;
      printf ( "\nValue of j = %f", j ) ;
      printf ( "\nValue of k = %c", k ) ;
      x = &i ;
      y = &j ;
      z = &k ;
      printf ( "\nOriginal address in x = %u", x ) ;
      printf ( "\nOriginal address in y = %u", y ) ;
      printf ( "\nOriginal address in z = %u", z ) ;
 x++ ;
 y++ ;
 z++ ;
      printf ( "\nNew address in x = %u", x ) ;
      printf ( "\nNew address in y = %u", y ) ;
      printf ( "\nNew address in z = %u", z ) ;
}

Here is the output of the program.
Value of i = 3
Value of j = 1.500000
Value of k = c
Original address in x = 65524
Original address in y = 65520
Original address in z = 65519
New address in x = 65526
New address in y = 65524
New address in z = 65520

Observe the last three lines of the output. 65526 is original value in
x plus 2, 65524 is original value in y plus 4, and 65520 is original
value in z plus 1. This so happens because every time a pointer is
incremented it points to the immediately next location of its type.
That is why, when the integer pointer x is incremented, it points to
an address two locations after the current location, since an int is
always 2 bytes long (under Windows/Linux since int is 4 bytes
long, new value of x would be 65528). Similarly, y points to an
address 4 locations after the current location and z points 1
location after the current location. This is a very important result
and can be effectively used while passing the entire array to a
function.
The way a pointer can be incremented, it can be decremented as
well, to point to earlier locations. Thus, the following operations
can be performed on a pointer:

(a)  Addition of a number to a pointer. For example,

 int  i = 4, *j, *k ;
j = &i ;
j = j + 1 ;
j = j + 9 ;
k = j + 3 ;

(b)  Subtraction of a number from a pointer. For example,
nt  i = 4, *j, *k ;
j = &i ;
j = j - 2 ;
j = j - 5 ;
k = j - 6 ;

(c)  Subtraction of one pointer from another.

 One pointer variable can be subtracted from another provided both variables point to elements of the same array. The resulting value indicates the number of bytes separating the corresponding array elements. This is illustrated in the following program.

 main( )
{
     int  arr[ ] = { 10, 20, 30, 45, 67, 56, 74 } ;
     int  *i, *j ;

     i = &arr[1] ;
     j = &arr[5] ;
     printf ( "%d %d", j - i, *j - *i ) ;
}

 Here  i and j have been declared as integer pointers holding addresses of first and fifth element of the array respectively. Suppose the array begins at location 65502, then the elements arr[1] and arr[5] would be present at locations 65504 and 65512 respectively, since each integer in the array occupies two bytes in memory. The expression j - i would print a value 4 and not 8. This is because j and i are pointing to locations
that are 4 integers apart. What would be the result of the expression  *j - *i? 36, since *j and *i return the values present at addresses contained in the pointers j and i.

(d)  Comparison of two pointer variablesPointer variables can be compared provided both variables
point to objects of the same data type. Such comparisons can be useful when both pointer variables point to elements of the same array. The comparison can test for either equality or inequality. Moreover, a pointer variable can be compared with zero (usually expressed as NULL). The following program illustrates how the comparison is carried out.

 main( )
{
     int  arr[ ] = { 10, 20, 36, 72, 45, 36 } ;
     int  *j, *k ;

     j = &arr [ 4 ] ;
     k = ( arr + 4 ) ;

     if ( j == k )
           printf ( "The two pointers point to the same location" ) ;
 else
           printf ( "The two pointers do not point to the same location" ) ;
}

A word of caution! Do not attempt the following operations on pointers... they would never work out.

(a)  Addition of two pointers
(b)  Multiplication of a pointer with a constant
(c)  Division of a pointer with a constant

Now we will try to correlate the following two facts, which we have learnt above:

(a)  Array elements are always stored in contiguous memory locations.
(b)  A pointer when incremented always points to an immediately next location of its type.
Suppose we have an array num[ ] = { 24, 34, 12, 44, 56, 17 }. The
following figure shows how this array is located in memory.
Here is a program that prints out the memory locations in which the elements of this array are stored.

main( )
{
  int  num[ ] = { 24, 34, 12, 44, 56, 17 } ;
  int  i ;

  for ( i = 0 ; i <= 5 ; i++ )
 {
    printf ( "\nelement no. %d ", i ) ;
  printf ( "address = %u", &num[i] ) ;
 }
}

The output of this program would look like this:

element no. 0 address = 65512
element no. 1 address = 65514
element no. 2 address = 65516
element no. 3 address = 65518
element no. 4 address = 65520
element no. 5 address = 65522

Note that the array elements are stored in contiguous memory locations, each element occupying two bytes, since it is an integer
array. When you run this program, you may get different addresses, but what is certain is that each subsequent address would be 2 bytes (4 bytes under Windows/Linux) greater than its immediate predecessor.
Our next two programs show ways in which we can access the elements of this array.

main( )
{
      int  num[ ] = { 24, 34, 12, 44, 56, 17 } ;
      int  i ;

      for ( i = 0 ; i <= 5 ; i++ )
 {
            printf ( "\naddress = %u ", &num[i] ) ;
            printf ( "element = %d", num[i] ) ;
 }
}

The output of this program would be:

address = 65512 element = 24
address = 65514 element = 34
address = 65516 element = 12
address = 65518 element = 44
address = 65520 element = 56
address = 65522 element = 17

This method of accessing array elements by using subscripted variables is already known to us. This method has in fact been given here for easy comparison with the next method, which accesses the array elements using pointers.

main( )
{
      int  num[ ] = { 24, 34, 12, 44, 56, 17 } ;
      int  i, *j ;

      j = &num[0] ;  /* assign address of zeroth element */

      for ( i = 0 ; i <= 5 ; i++ )
 {
            printf ( "\naddress = %u ", j ) ;
            printf ( "element = %d", *j ) ;
            j++ ;  /* increment pointer to point to next location */
 }
}

The output of this program would be:

address = 65512 element = 24
address = 65514 element = 34
address = 65516 element = 12
address = 65518 element = 44
address = 65520 element = 56
address = 65522 element = 17

In this program, to begin with we have collected the base address of the array (address of the 0th element) in the variable j using the statement,

j = &num[0] ;  /* assigns address 65512 to j */

When we are inside the loop for the first time, j contains the address 65512, and the value at this address is 24. These are printed using the statements,

printf ( "\naddress = %u ", j ) ;
printf ( "element = %d", *j ) ;

On incrementing j it points to the next memory location of its type (that is location no. 65514). But location no. 65514 contains the second element of the array, therefore when the printf( )statements are executed for the second time they print out the second element of the array and its address (i.e. 34 and 65514)...
and so on till the last element of the array has been printed. Obviously, a question arises as to which of the above two methods should be used when? Accessing array elements by pointers is always faster than accessing them by subscripts. However, from the point of view of convenience in programming we should
observe the following:
Array elements should be accessed using pointers if the elements are to be accessed in a fixed order, say from beginning to end, or from end to beginning, or every alternate element or any such definite logic.

Instead, it would be easier to access the elements using a subscript if there is no fixed logic in accessing the elements. However, in this case also, accessing the elements by pointers would work faster than subscripts.

No comments:

Post a Comment