A tutorial on pointers


Match word(s).

If you have any questions or comments,
please visit us on the Forums.

FAQ > Explanations of... > A tutorial on pointers

This item was added on: 2003/02/24

The following article was written by Stoned_Coder

  • What is a pointer?

  • How do I declare a pointer in my c/c++ code?

  • Can pointers point to functions?

  • OK. So now I have a good idea how to declare a simple pointer, how do I use them. How do I get the object being pointed at?

  • So is there some relationship between operator * and operator & ?

  • Can you apply operator * to an object or operator & to a pointer.

  • Can several pointers all point at the same object?

  • How should we signify that a pointer doesn't point to anything?

  • Can we set one pointer to equal another pointer?

  • What is a void* ?

  • Explain how const works with pointers.

  • So what's this pointer arithmetic you have mentioned?

  • Can we talk about function pointers now?

  • What is a smart pointer?

  • What is passing by value?

  • So what is passing by reference?

  • Supplimentary Examples

  • Q. What is a pointer?

    A pointer is a variable just like any other. It holds a memory address and no matter what type it is pointing to, the size of a pointer is constant, 32 bits on a 32bit system.


    Q. How do I declare a pointer in my c/c++ code?

    For a given type T a pointer to T is a T*. So here are some examples.

    
    int     myint;
    double  mydouble;
    long    mylong;
    int     *pointer_to_myint = &myint; // & is the address of operator in this case.
    double  *pointer_to_mydouble = &mydouble;
    long    *pointer_to_mylong;         // you do not need to initialise pointers
                                        // as you have to with references in c++
    pointer_to_mylong = &mylong;        // you can do this if need be.
    
    


    Q. Can pointers point to functions?

    Yes they can. We will deal with this later when we understand pointers better.


    Q. OK. So now I have a good idea how to declare a simple pointer, how do I use them. How do I get the object being pointed at?

    To get at the object being pointed to by a pointer you have to dereference it. This is where most newbies suffer the most confusion. It is simple I promise you. Given a pointer P then the object being pointed to is *P. This might be easier to see in code...

    
    int myintA, myintB;
    int *pmyint = &myintB;
    
    myintA  = 10;              // set myintA normally.
    *pmyint = 20;              // set myintB through pointer dereference.
    pmyint  = &myintA;         // now pmyint points to myintA and not myintB
    cout << *pmyint << endl;   // print contents of myintA by dereferencing pointer.
    *pmyint = 100;             // set myintA through pointer dereference.
    cout << *pmyint << endl;   // print contents of myintA again by dereferencing pointer.
    
    


    Q. So is there some relationship between operator * and operator & ?

    Yes. operator & is the address of operator. You can use it to turn an object into a pointer. Operator * is the dereference operator. You use it to turn a pointer back into an object.


    Q. Can you apply operator * to an object or operator & to a pointer.

    You cannot dereference an object. Thats an illegal operation. You can however take the address of a pointer. This surprisingly enough is called a pointer to a pointer. This is sometimes necessary to do especially in C when you have to pass a pointer by reference into a function. Simple meaningless snippet to illustrate..

    
    int A;              // an object
    int * pmyint = &A;  // pointer to int
    int** pointer_to_pointer = &pmyint; // pointer to pointer to int
    
    


    Q. Can several pointers all point at the same object?

    Of course. You can have an unlimited number of pointers to a single object.


    Q. How should we signify that a pointer doesn't point to anything?

    Set the pointer to NULL or 0.


    Q. Can we set one pointer to equal another pointer?

    If they are of the same type then yes otherwise you must cast to the correct type to do this.


    Q. What is a void* ?

    A void* is a generic pointer type. It's a pointer but the type pointed to is unknown. You may not dereference a void* but instead must first cast it to another pointer type before you dereference. Also you may not use a void* in any form of pointer arithmetic.


    Q. Explain how const works with pointers.

    OK. This is another source of confusion for the newbies. It's simple enough once you see it in action. To explain this we need to touch on pointer arithmetic. Don't worry we will come back to that later in more detail. Firstly when we talk about const with pointers we have to make a distinction between the pointer and the object being pointed at. This snippet will explain in code....

    
    const int cintA = 10;                     // A constant int
    int       intB = 20;                      // A nonconstant int
    const int *pointer_to_const_int = &cintA; // A pointer to a const int. The POINTER ITSELF IS NOT CONST.
    *pointer_to_const_int = 20;               // Compiler error *pointer_to_const_int is a constant 
                                              // and cannot be changed.
    pointer_to_const_int++;                   // This is fine, it's simple pointer arithmetic 
                                              // moving the pointer to the next memory location.  
                                              // The pointer itself is not const so it may be repointed.
    const int *const  const_pointer_to_const_int = &cintA;  // Here we have a constant pointer to a 
                                              // constant int. Notice as pointer is const we must 
                                              // initialise it here.
    const_pointer_to_const_int++;             // This is now illegal. The pointer itself is const 
                                              // and may not be repointed.
    *const_pointer_to_const_int = 40;         // Again compiler error. Pointer points to constant data. 
                                              // You cannot write through this pointer.
    int *pointer_to_int = &intB;
    *pointer_to_int = 40;                     // This is fine. Data pointed to is not constant.
    pointer_to_int++;                         // Again fine, pointer is not constant, you may repoint it.
    int *const  const_pointer_to_int = &intB; // Constant pointer to non-constant int. 
                                              // Pointer is const so must be initialised here.
    const_pointer_to_int++;                   // Compiler error. The pointer itself is constant and 
                                              // may not be repointed.
    *const_pointer_to_int = 80;               // This is fine. Although the pointer is constant the object
                                              // pointed to is not constant and so may be written to.
    
    

    So in recap if the const appears before the * then it relates to the object and if the const appears after the * it relates to the pointer itself.


    Q. So whats this pointer arithmetic you have mentioned?

    Lets think about an array for a minute. Arrays have a type. It is this type that governs the size of a single element. The number of elements multiplied by the sizeof( arraytype ) is the amount of contiguous memory that is needed to store the array. The name of the array is nothing more than a disguised constant pointer to the first element of the array or array[0] in c/c++ terms. If we wish to access the second element of our array, in c we would use the term array[1]. Lets examine what exactly the [] does. This is actually shorthand for *(array+number_inside_[]). This is pointer arithmetic but it is disguised with a friendly face, operator [].

    Addition and subtraction are allowed with pointers. When you add an integer to a pointer you are moving the memory location pointed at by the integer number multiplied by the sizeof (pointertype). For instance if I have a char* and I add 1 to it I have advanced the pointer by sizeof(char) bytes. If I add 1 to an int* then I will have advanced the pointer by sizeof(int) bytes. Subtraction does the same except it retreats the pointer by sizeof(pointertype). You can also subtract 1 pointer from another to get the spacing between them. C++ and C provide a special type for this particular calculation. Its called ptrdiff_t and is found in cstddef or stddef.h.

    Illustration in code of pointer arithmetic....

    
    int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int *parray = array;              // remember the array name acts as a pointer so no need for &array[0]
    for (int i = 0; i < 10; i++)
    {
      cout << *(parray + i) << endl;  // walk array using pointer arithmetic
      cout << array[i] << endl;       // more normal terminology but same result as above.
    }
    parray = &array[9];               // lets start at the end and go backwards this time.
    for (int i = 0; i < 10; i++)
    {
      cout << *(parray - i) << endl;  // walk backwards through array using pointer arithmetic.
      cout << array[(9 - i)] << endl;
    }
    
    

    Naturally you can also use both forms of operator ++ and operator -- on pointers.

    Remember also that void* pointers cannot be used for pointer arithmetic.

    Not strictly pointer arithmetic but you can also compare pointers with the usual comparison operators !=,==,<,>,<=,>= . This can be a very useful facility sometimes.


    Q. Can we talk about function pointers now?

    Sure now that we have covered simple pointers lets look at function pointers. Just as an array name is a constant pointer to an array, a function name is similarly a constant pointer to the starting address in memory of the function. Declaring a function pointer can be a bit tricky and again another major source of confusion to the newbies. It looks like this...

    return_type (*pointer_to_function_name) (params)

    For instance, look at this small working program.....

    
    #include <iostream> 
    #include <iomanip> 
    
    using namespace std;
    
    void Swap(int *const, int *const);
    // On the following prototype, the third parameter is a 
    // pointer to a function that takes 2 ints and returns a bool.
    void BubbleSort(int[], const int, bool(*) (int, int));
    bool Ascending(int, int);
    bool Descending(int, int);
    
    int main(void)
    {
      int       array[] = { 20, 10, 30, 50, 70, 60, 80, 90, 110, 140, 120, 130, 200 };
      const int ArraySize = sizeof(array) / sizeof(array[0]);
      
      cout << "1 to sort ascending or 2 to sort descending: ";
    
      int choice;
      cin >> choice;
      
      for (int i = 0; i < ArraySize; ++i) 
      {
        cout << setw(6) << array[i];
      }
      
      cout << endl;
      
      if (choice == 1)
        BubbleSort(array, ArraySize, Ascending);
      else if (choice == 2)
        BubbleSort(array, ArraySize, Descending);
      else
      {
        cout << "Blithering Idiot!" << endl;
        return(0);
      }
    
      for (int i = 0; i < ArraySize; ++i) 
      {
        cout << setw(6) << array[i];
      }
      
      cout << endl;
      
      return(0);
    }
    
    void Swap(int *const ptr1, int *const ptr2)
    {
      int temp = *ptr1;
      *ptr1 = *ptr2;
      *ptr2 = temp;
    }
    
    bool Ascending(int a, int b)
    {
      return(b < a);
    }
    
    bool Descending(int a, int b)
    {
      return(b > a);
    }
    
    void BubbleSort(int array[], const int ArraySize, bool (*compare) (int, int))
    {
      for (int pass = 1; pass < ArraySize; ++pass)
      {
        for (int single_pass = 0; single_pass < ArraySize - 1; ++single_pass)
        {
          if ((*compare) (array[single_pass], array[single_pass + 1])) 
            Swap(&array[single_pass], &array[single_pass + 1]);
        }
      }
    }
    
    

    Here you can see function pointers in action. This is not a great example but was quick to put together. Go to the full function pointer tutorial for more!


    Q. What is a smart pointer?

    A smart pointer is a user defined class in c++ that acts as a pointer but is in fact an object. This has some important uses when you want to write exception-safe code. There are many uses for smart pointers and they warrant a very in depth article in themselves.

    For more on smart pointers, see the tutorial on using the auto_ptr smart pointer.


    Q. What is passing by value?

    To explain this we must first look at exactly what happens when a function is called, so let's look at a simple example:

    
    int func (int a, int b)
    {
       return (a + b);
    }
    
    

    Now as you can see we've made a function that takes 2 ints and returns their sum. What happens when we call this. Well firstly the parameters for the function must be COPIED to the stack. Then the function code executes and the return value is COPIED to the stack. So if we say:

    
    int a = 10;
    int b = 20;
    int c = func(a,b);
    
    

    then inside the code for func() we're manipulating copies of a and b and not a and b themselves. This is passing by value. Any changes we make to a and b within the function will not be reflected to a and b in the calling function.This can be observed by running this simple program.

    
    #include <iostream> 
    
    using namespace std;
    
    void ByVal(int a)
    {
      a++;
      cout << " Value of a in ByVal is " << a << endl;
    }
    
    int main(void)
    {
      int a = 10;
      cout << "Value of a in main is " << a << endl;
      ByVal(a);
      cout << "Value of a in main is " << a << endl;
      return(0);
    }
    
    /*
     * Program output
     Value of a in main is 10
     Value of a in ByVal is 11
     Value of a in main is 10
     *
     */
    
    

    In C all parameter passing is by value. Soon we will see how to get around that.


    Q. So what is passing by reference?

    This answer is split in two. First, an answer that suits both C and C++, then one for C++ only.

    When we pass by reference we are not working with copies of our data but instead the data itself. Therefore any changes made to our data in a function WILL BE REFLECTED BACK TO THE CALLING FUNCTION. To accomplish this in C you have to use pointers.

    Instead of passing our data, we instead pass our data's address in memory. Look at this small snippet.

    
    void func (int* a)
    {
      (*a)++;
    }
    
    

    Here we have a small function that takes a pointer (by value) and uses it to "reference" the data we want to play with. This time the increment to *a is reflected back to the calling function. Please note that the pointer a is passed by value. So, what if we wanted to pass a pointer by reference? Well there is an answer to this, you pass a pointer to a pointer.

    
    void func(int **a)
    {
      (*a)++;
    }
    
    

    This would modify a pointer by reference. The pointer to pointer is passed by value but you can use that to "reference" the pointer you want to play around with. Here's a small program so that you can see it in action.

    
    #include <stdio.h> 
    
    void ByRef(int *a)
    {
      (*a)++;
      printf("The value of *a in ByRef is %d\n", *a);
    }
    
    int main(void)
    {
      int a = 10;
      printf("Value of a in main is %d\n", a);
      /* 
       * pass a pointer to a. remember that & turns an object into a pointer by taking its address
       */
      ByRef(&a);
      printf("Value of a in main is %d\n", a);
      return(0);
    }
    
    /*
     * Program output
     Value of a in main is 10
     The value of *a in ByRef is 11
     Value of a in main is 11
     *
     */
     
    

    And now to the C++ only answer.

    Everything above is valid for C/C++ but in C++ we also have reference parameters. These do what pointers do but are much easier to use and less prone to errors. Here's how you pass by reference in C++.

    
    void func(int &a)
    {
      a++;
    }
    
    

    Notice the only difference between this and the by value alternative is we pass an int& rather than an int. int& means "reference to int". Behind the scenes of references the compiler is playing with pointers but that means we don't have to.

    A runnable example of reference passing C++ style:

    
    #include <iostream> 
    
    using namespace std;
    
    void ByRef(int &a)
    {
      a++;
      cout << " Value of a in ByRef is " << a << endl;
    }
    
    int main(void)
    {
      int a = 10;
      cout << "Value of a in main is " << a << endl;
      ByRef(a);
      cout << "Value of a in main is " << a << endl;
      return(0);
    }
    
    /*
     * Program output
     Value of a in main is 10
     Value of a in ByRef is 11
     Value of a in main is 11
     *
     */
    
    



    Credit: Stoned_Coder


    Supplimentary Examples

    Here are some additional examples supplied by golfinguy4 to suppliment Stoned_coder's paper:

    
    #include <iostream> 
    
    using std::cout;
    using std::endl;
    
    int main(void)
    {
      int *foo;       //declares a pointer to an int
      foo = new int;  //means that foo points to an int on the heap/free store
      *foo = 4;       //the address that foo is pointing too will now hold a value of 4
    
      // Now here is where it all comes together
      cout  << "The address of the pointer is " << &foo <<"." << endl 
            << "The address the pointer is pointing to is " <<foo <<"." <<endl 
            << "The value that is held in the address that the pointer is pointing to is " <<*foo <<"." <<endl;
    
      delete foo;
      
      return(0);
    }
    
    /*
     * Program output
     The address of the pointer is 0012FF88.
     The address the pointer is pointing to is 00853144.
     The value that is held in the address that the pointer is pointing to is 4.
     *
     */
     
    

    
    #include <iostream> 
    
    using std::cout;
    using std::endl;
    
    int main(void)
    {
      int *foo;       //again, we have a pointer to an int
      int number;     //just a regular int
      foo = &number;  //foo now points to number's address
    
      //now, any change to foo will also change number
      number = 5;
      cout << "The address that number is at is " << &number << endl;
      cout << "This address being pointed to by foo " << foo << endl;
      cout << "number= " << number << endl;
      cout << "(accessed through foo) number=  " << *foo << endl << endl;
    
      number = 2;
      cout << "number= " << number << endl;
      cout << "*foo= " << *foo << endl << endl;
    
      *foo = 10;
      cout << "Number= " << number << endl;
      cout << "*foo= " << *foo << endl;
    
      return(0);
    }
    
    /*
     * Program output
     The address that number is at is 0012FF80
     This address being pointed to by foo 0012FF80
     number= 5
     (accessed through foo) number=  5
     
     number= 2
     *foo= 2
     
     Number= 10
     *foo= 10
     *
     */
    
    

    Script provided by SmartCGIs