Last updated: Tuesday 15th March 2005, 5:31 PT by AHD

 

1.     Introduction to Pointers

 

 

Cast your mind back to our first introduction to variables.  You already know that a variable has a name and a data type.  Variables are stored in memory, and the size of memory used to store a variable depends on the type of the data to be stored.  Each byte of memory has an address which the compiler uses to locate a variable and its contents.

 

 

A "pointer" is a special kind of variable.  A pointer is a variable that contains the address of another variable.  Because a pointer contains the address of a variable it is said to "point to" that variable.

 

 

This document introduces the important topic of pointers in C and C++.  You will learn how to declare and initialize a pointer, and how to use pointers that point to simple variables.  If you continue your studies of the C or C++ language by taking an advanced course, you will study pointers in detail, use pointers with arrays and strings, pass pointers to functions, use arrays of pointers, pointers to pointers, pointers to structures and pointers to functions.  In fact, an advanced course will devoted some considerable time to the use of pointers because the C/C++ language can only be used to its full potential by using pointers.

 

1.1     The Use of Pointers in C and C++

 

 

 

All serious C and C++ programs use pointers.  In fact, there are some operations which cannot be performed without using a pointer.  For instance, pointers can access variables that would not normally be visible to a function and pointers enable multiple values to be returned from functions.

 

 

The following is a list of common uses of pointers:

 

·       returning more than one value from a function

 

·       manipulating strings

 

·       accessing variables not normally visible

 

·       array element manipulation

 

·       passing the address of one function to another function

 

·       dynamic memory allocation

1.2     Pointers to Simple Variables

 

 

 

This section uses the program pointers01.cpp to demonstrate how to declare and initialize a pointer variable, and how pointers are used with simple variables (ones which store a single value).

 

 

// Program: pointers01.CPP

// Programmer: A.H.Dawson

// Date: Tuesday 11th November 2003, 8:57 PT

// Program to demonstrate the use of pointers

 

#include <iostream.h>

#include <conio.h>

#include <stdio.h>

 

void main (void)

{

   char letter = 'A';

   char *ptr;

   ptr = &letter;

   /* printf is a C function used to print to the screen the data in the () */

   /* I am using it in this program instead of the C++ cout so that */

   /* I can display the value of the pointer - the address, as an integer */

   printf( "letter = %c \n", letter );

   /* %c is the format code for printing a character */

   printf( "&letter = %u \n", &letter );   /*  %u  is the format code  */

   printf( "ptr = %u \n ", ptr );              /*  for an unsigned integer */

   getch();

}

 

 

The output from pointers01.cpp is as follows:

 

 

Note: when you run the code you will get a different value for the address (not 6618627) - but &letter and ptr will always have the same value.

 


 

The first statement in pointers01.cpp declares a variable named letter and initializes it with the value 'A'.

 

char letter = 'A';

 

 

The second statement declares a pointer variable:

 

 

char *ptr;

 

 

This declaration indicates that ptr is the name a pointer variable which will contain the address of an char variable.  When used in an expression, the pointer variable ptr represents an address. 

 

 

The  *  placed in front of the name ptr is the address operator known as the indirection operator.  The indirection operator is placed in front of the name ptr to indicate that this variable is a pointer variable.   The indirection operator is used in two ways in C/C++.   In a declaration such as the one shown above, it simply means "this variable is a pointer".  You will see its second use later in this section.

 

 

The third statement of pointers01.cpp uses the other address operator known as the address-of operator (&):

 

 

ptr  =  &letter;

 

 

This statement results in the address of the variable letter being assigned to the pointer variable ptr.  The address of a variable is allocated by the compiler.  If you compile and run pointers01.cpp yourself, the address displayed will be different to the one shown in the output display above.


 

The following diagram illustrates the concept of pointer variables.

 

 

The variable named letter is of type char, and hence uses one byte of memory.  Let's assume that the address of this byte of memory is 8711.  The variable letter contains the value 'A'. 

 

 

 

                                    letter

                              (address = 8711)

                                        

                                      'A'

 

The lines:

 

 

char  *ptr;

ptr = &letter;

 

 

would then generate a pointer variable named ptr, and the address of the variable letter is stored in ptr:

 

 

                            ptr

                                        

                          8711

 

 

The output statements of the program pointers01.cpp

 

 

 

 

     printf( "letter = %c \n ", letter );

   printf( "&letter  = %u \n ", &letter );   /*   %u  is the format code  */

   printf( "ptr = %u \n ", ptr );            /*    for an unsigned integer */

 

 

. . . print out the contents of the variable letter ('A'), the address of the variable letter  (8711), and the contents of the variable ptr (8711).


The second use of the indirection operator is illustrated in the following program – pointers02.cpp.   If the indirection operator is used in front of a pointer variable in a statement which is not a declaration, it refers to the value contained at the address the pointer stores.  Consider the following program:

 

 

/* Program: pointers02.CPP */

/* Programmer: A.H.Dawson */

/* Date Tuesday 11th November 2003, 9:09 PT */

/* Program to demonstrate the use of pointers */

/* Note: C++ is a superset of C */

 

#include <iostream.h>

#include <conio.h>

#include <stdio.h>

 

void main (void)

{

   char letter = 'A';

   char *ptr;

   ptr = &letter;

   printf( "letter = %c \n", letter );

   printf( "&letter  = %u \n", &letter ); /* %u  is the format code */

   printf( "ptr  = %u \n", ptr );         /* for an unsigned integer */

   printf( "*ptr  = %u \n", *ptr );       /* value as an unsigned integer */

   printf( "*ptr  = %c \n", *ptr );       /* value as a char  */

   getch();

}

 

 

The output from this code is as follows:

 

 

 

The last two lines of program pointers02.cpp result in the value stored at the address contained by the pointer variable ptr being displayed on the screen.  Note that a character is stored as the integer ASCII code, and can be displayed as an integer or as a character.  The ASCII code for the character ‘A’ is 65.

 

 

 

1.3     Pointer Declarations

 

 

 

Like any other variable, a pointer variable must be declared before it is used.  The value of a pointer variable (the address it contains) may also change during the course of the program run.  A pointer variable can have any legal identifier name.  The following line shows the pointer declaration of program pointers02.cpp:

 

 

   char *ptr;

 

 

It looks just like any other variable declaration with a variable name and a type.  The declaration states that the program has a pointer variable named ptr that can point to a data object of type char. Pointers don't have a type in the same sense that a simple variable has a type.  The type stated in a pointer declaration refers to the type that the pointer points to, but the pointer variable always contains an address.  Hence, the bytes storage space required for a pointer is always the same for any particular computer system, usually the same storage space as an integer (two or four bytes depending on the implementation).  Just like any other variable, a pointer variable is stored at an address allocated by the compiler.  This means that a pointer variable can point to another pointer variable which in turn can point to another, and so on.

 

 

To declare pointers to different types of variables, again the procedure is similar to simple variable declarations.  The following statements are examples of pointer declarations to type int and float variables:

 

 

int *i_ptr, *i_ptr_2;

float *f_ptr;

 

Note that if you declare more than one pointer on the same line, they both must be preceded by the indirection operator.

 

 

 In most cases you would declare a pointer to a particular type, such as char.  However, you can also declare a pointer to type void, which allows it to point to any type of object.

 

 


1.4     Pointer Initializations

 

 

After declaring a pointer variable, storage space is reserved for it and this storage space will contain random meaningless data until you initialize the pointer.  You initialize a pointer variable with a statement such as:

 

 

ptr = &value;

 

 

The statement above assigns the address of value to ptr.  ptr then stores the address where value is stored.

 

 

The third line of code in the following extract from the program pointers02.cpp shows the pointer variable ptr being initialized with the address of the char variable letter.

 

 

     char letter =  'A';

   char  *ptr;

   ptr = &letter;

   printf( "  letter = %c \n ", letter );

   printf( " &letter  = %u \n ", &letter );   /*   %u  is the format code  */

   printf( "  ptr  = %u \n ", ptr );         /*    for an unsigned integer */

 

 

The last two printf statements result in the address of the variable letter printed out to the screen. 

 

The line . . .

 

     printf( " &letter  = %u \n ", &letter );   /*   %u  is the format code  */

 

prints:

 

     &letter = 8737                   (Note: the actual number will be different on different machines)

 

using the address-of operator to obtain the address of letter directly, and the line . . .

 

     printf( "  ptr  = %u \n ", ptr );      /* for an unsigned integer */

 

prints:

 

     ptr = 8737

 

using the contents of the pointer variable ptr.

The relationship of the variable letter to the initialized variable ptr is shown in the following illustration:

                                                                                             

 

 

 

 

                     8737                                    'A'

 

                            ptr                       letter

 

 

 

The value in the variable ptr is the start address of the variable letter.  The arrow linking the two variables is a convention often used in text books on the C/C++ programming language to illustrate this connection between a pointer variable and the variable it points to.

 

You should take care not to use uninitialized pointers.  Since a pointer which has not been initialized may contain any address - including an operating system address, any reference to an uninitialized pointer may change the contents of operating system memory, and cause the system to malfunction.  Unpredictable consequences, such as for example an unexpected reboot of the computer, may result.

 

 

 

Program pointers02.cpp shows that the pointer variable ptr is assigned an address constant (the address of letter obtained with the address-of operator):

 

 

   char  *ptr;

   ptr = &letter;

 

 

 

 

A pointer variable may also be assigned the value of another pointer variable, as shown in the following example lines of code:

 

 

 

   char  *ptr, *ptr2, *ptr3;

   ptr = &letter;

   ptr2 = ptr;

   ptr3 = ptr2;

 


1.5     Using a Pointer to change the value of a Variable

 

 

Once we have a pointer that points to a variable, we have two ways to access the data in that variable.  The usual way is the direct way, by using the variable's name as shown below:

 

int number = 10;

printf( "number = %d \n" , number );

 

 

If we extend the code to include a pointer to int, and assign to the pointer the address of the variable number, then we can access the data in number by using the pointer.  This sort of access is known as indirect access, and uses the indirection operator:

 

 

int  *ptr;

int number = 10;

ptr = &number;

printf( "number = %d \n" , number );

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

 

 

Both of the printf statements will access the same variable (number), the first by direct access and the second by indirect access.

 

In the same way that you can change the contents of a variable by using its name as in:

 

number  =  10;

 

you could equally well use a pointer to the variable instead:

 

*ptr  = 10;

 

The following page shows a program which illustrates this point.

1.6     Test your knowledge

 

Before you run the following program, study the code and try to predict the output.

 

/* Program: pointers03.cpp */

/* Programmer: A.H.Dawson */

/* Date: Tuesday 11th November 2003, 9:19 PT */

/* Program to demonstrate the use of pointers */

 

#include <iostream.h>

#include <conio.h>

#include <stdio.h>

 

void main (void)

{

   int number1 = 50;

   int number2 = 100;

   int *ptr1, *ptr2 ;

   ptr1 = &number1;

   ptr2 = &number2;

   printf( "number1 = %d \n", number1 );

   printf( "number2 = %d \n", number2 );

   printf( "*ptr1 = %d \n", *ptr1);

   printf( "*ptr2 = %d \n", *ptr2);

   ptr2 = ptr1;

   printf( "*ptr1 = %d \n", *ptr1);

   printf( "*ptr2 = %d \n", *ptr2);

   *ptr2 = 75;

   printf( "*ptr1 = %d \n", *ptr1);

   printf( "*ptr2 = %d \n", *ptr2);

   getch();

}


 

1.7     Pointer Arithmetic

 

A pointer is an address, an address is an integer, but a pointer is not an integer in the true sense of the word integer.  You cannot do regular arithmetic on a pointer as if it was a true integer.  So what is pointer arithmetic?  To illustrate pointer arithmetic, let’s revisit arrays…

 

Imagine the elements of an empty array as a line of empty boxes standing side by side.  Each box can hold one byte of data, and each box has an address, which is a number giving the location of the box in memory.

 


    
4052       4053      4054       4055      4056

 

 

 


The above bytes of memory might be reserved for example . . .

 

 

after a declaration such as . . .

 

 

   char my_array[5];

 


Consider this program:

 

/*

   Program: pointers04.cpp

   Programmer: A.H.Dawson

   Date: Tuesday 11th November 2003, 9:23 PT

   Program to demonstrate the use of pointers

*/

 

#include <iostream.h>

#include <conio.h>

#include <stdio.h>

 

void main (void)

{

   char my_array[5];

   int i;

 

   my_array[0] = 'A';

   my_array[1] = 'B';

   my_array[2] = 'C';

   my_array[3] = 'D';

   my_array[4] = 'E';

 

   for ( i = 0; i < 5 ; i = i + 1 )

   {

     printf( " my_array[%d] =  %c ", i, my_array[i] );

     printf( "\t &my_array[%d] =  %u \n", i, &my_array[i] );

     /* %u is printf format code for unsigned integer */

   }  /*  %t is printf format code for tab */

   getch();

}

 

This program will set up an array using 1 byte of memory for each element.  The first element of the array is assigned a value equal to the character 'A',  the second element is assigned the character 'B' and so on. 

 

This is the output on running the code:

 

 

Notice the addresses go up in one byte chunks. The address in memory at which the first element of an array starts is automatically set by the computer.  Subsequent elements will have an address relative to the start address.  For elements with a storage size of one byte this would be the start address + 1, start address + 2, and so on.   If the start address of the array named my_array was  8710, the addresses of the elements would be as shown in the following diagram.

 

 


    
8710        8711      8712       8713      8714

       A     B    C     D     E

 

 

If the elements were 2 bytes in size (the size for storage of integers on some machines), then the addresses of the elements would be in steps of two: 4052, 4054 etc.  The computer automatically handles the allocation of memory addresses for arrays, so you don't need to worry about that.

 

Consider this program:

 

// Program: pointers05.cpp

// Programmer: Anne Dawson

// Date: Tuesday 11th November 2003, 9:29 PT

// Program to demonstrate the use of pointers

 

#include <iostream.h>

#include <conio.h>

#include <stdio.h>

 

void main (void)

{

     int my_array[5];

     int i;

 

     my_array[0] = 1;

     my_array[1] = 2;

     my_array[2] = 3;

     my_array[3] = 4;

     my_array[4] = 5;

 

     for ( i = 0; i < 5 ; i = i + 1 )

     {

          printf( " my_array[%d] =  %u ", i, my_array[i] );

          printf( "\t &my_array[%d] =  %u \n", i, &my_array[i] );

          /* %u is printf format code for unsigned integer */

     }  /* %t is printf format code for tab */

   getch();

}

This is the output on running the code:

 

 

 

 

 

Notice the addresses go up in four byte chunks, (the storage requirements may be different on different machines, e.g. two bytes) because we now have an array of integers, and each integer occupies 4 bytes on my machine. If the address of the first element was 8706, the memory would look like this:

 


    
8706       8710      8714       8718       8722

       1     2      3      4     5

 

 

 

So getting back to pointer arithmetic…

 

 

If I declare a pointer to type int, then assign to it the address of the first element of an array, I can now refer to the elements of the array by using the pointer.  For example, consider the following code and its output:

 

/*

   Program: pointers06.cpp

   Programmer: Anne Dawson

   Date: Tuesday 11th November 2003, 9:38 PT

   Program to demonstrate the use of pointers

*/

 

#include <iostream.h>

#include <conio.h>

#include <stdio.h>

 

void main (void)

{

   int my_array[5];

   int i;

   int *ptr;

 

   my_array[0] = 1;

   my_array[1] = 2;

   my_array[2] = 3;

   my_array[3] = 4;

   my_array[4] = 5;

 

   for ( i = 0; i < 5 ; i = i + 1 )

   {

     printf( "my_array[%d] =  %u \n", i, my_array[i] );

   }

 

   ptr = &my_array[0];

   printf( "my_array[0] contains %u \n",*ptr );

 

   ptr++; /* THIS IS POINTER ARITHMETIC */

   /* the above statement increases the value of ptr */

   /* by a value equal to the size in bytes of the data */

   /* it points to. So for an integer the address increases */

   /* by 4 bytes, thus the pointer now points to the next element */

   /* in the array */

 

   printf( "my_array[1] contains %u \n",*ptr );

   getch();

}

 

 

 

The output:

 

 


 

Explanation:


    
8706       8710      8714       8718       8722

       1     2      3      4     5

 

 

The following line of the program puts the address of the first element (8706) into the pointer ptr.

 

     ptr = &my_array[0]; 

 

 

The following line of the program prints out the contents of the address held by ptr (1).

     printf( "my_array[0] contains %u \n",*ptr );

 

The followng line is an example of pointer arithmetic:

 

     ptr++;

 

This statement increases the value of ptr by a value equal to the size in bytes of the data to. So, since the pointer points to an integer the address the pointer contains increases by 2 bytes, thus the pointer now points to the next element in the array (address 8710).  The contents at this new address are printed out by the following line of code:

 

   printf( "my_array[1] contains %u \n",*ptr );

 

Note: you may also decrement a pointer using ptr--.  It works in exactly the same way.


 

1.8     Conclusion

 

This document is complementary to the chapter in the textbook on "Pointers and Dynamic Arrays" (chapter 12 or 11, depending on the edition).  You should ensure that you read the first two sections of this chapter as it contains additional material not covered in this document.  The third section of the chapter ("Classes and Dynamic Arrays") is not covered in this course.