Pointers



Pointers: A pointer is a variable that contains the address of another variable.

Why Use Pointers?
  1. They allow us to return more than one value from a function call by passing pointers to variables into the function as arguments.
  2. To manipulate arrays more easily by moving pointers to their elements instead of the whole element.
  3. To create complex data structures such as linked lists, binary trees, etc.


Pointers in Memory

To get a better understanding of what pointers are and what they do let's take a look at pointers on a very basic level. Let's look at pointers and other types of variables as they appear in memory. Memory consists of a sequential array of bytes. Each consisting of 8 bits. Each bit is like an on-off switch. Each byte therefore can have 256 different combinations of bits turned either on or off (see the page on Decimal-Hexadecimal-Binary Numbering Systems for more information.) These bytes of memory are numbered from zero and up using the hexadecimal numbering system. In figure 1 we see a portion of memory diagrammed. Lower address are at the top of the diagram.

          


Figure 1. Somewhere in Memory


Now suppose you declare the following variables in main():


           int ivar;
           double d_var;
           char c_var;
           char c_array[5];

Memory will now appear as in figure 2. Since no values have been stored yet the contents of these variables is garbage. Note: some compilers produce code that automatically sets all numerical variables to zero and clears all bytes in a character array to null terminators, i.e. byte value zero.
          


Figure 2. After declaring variables.


If you execute the following code to store some values in these variables then memory will appear as in figure 3.


           ivar = 16;
           d_var = 3.14159;
           c_var = 'a';
           strcpy(c_array, "test");

          


Figure 3. After storing values in the variables.


Now let's see what memory looks like as we declare the same set of variables and some pointers in main():


           int ivar;
           double d_var;
           char c_var;
           char c_array[5];
           int *i_ptr;
           double *d_ptr;
           char *c_ptr;

Memory will now appear as in figure 4.
          


Figure 4. After declaring variables and pointers.


Now when we execute the following code to store some values in these variables and in the pointers memory will appear as in figure 5.


      ivar = 16;
      d_var = 3.14159;
      c_var = 'a';
      strcpy(c_array, "test");
      i_ptr = &i_var;
      d_ptr = &d_var;
      c_ptr = &c_var;

Notice that we are storing the starting memory address of the integer variable i_var in the integer pointer variable i_ptr and the same goes for the other pointers. We use the ampersand & as the address operator to indicate the address of a variable in memory, instead of the value stored in the variable.



Figure 5. After storing values in the variables and pointers.


When we want to access the value stored in a variable using a pointer which stores the address of that variable we use the asterisk, '*'. This is referred to as dereferencing the pointer.


      printf("%d", *i_ptr); /* Print the value stored in i_var */
      printf("%f", *d_ptr); /* Print the value stored in d_var */
      printf("%c", *c_ptr); /* Print the value stored in c_var */



Using Pointers In Function Calls

When you call a function you pass arguments to that function. If we consider the technical aspects of computer programming separate from the C language we find that there are two ways in which those arguments can be passed to the function.

The first way of passing arguments to a function is Pass By Value. In this approach the values stores in the variables, which are arguments to the function, are copied and the copies passed to the function. The function therefore only has the values. It does not have any way of accessing or changing the variables, in the calling function, from which the values are copied.

The second way of passing arguments to a function is Pass By Reference. In this approach the arguments passed to the function are "references" to the variables that are arguments. The called function can now make changes in the variables themselves. These "references" are actually the memory addresses of the variables.

In C we can only Call By Value, but we can simulate the Call By Reference by passing the values stored in pointers, i.e. memory addresses of variables, into a function.




If you execute the following code in main() and then call a function named ValFunc() memory accessed by main() will look like that illustrated in figure 6

         int i_var;
         double d_var;
         char c_var;
         i_var = 16;
         d_var = 3.14159;
         c_var = 'a';
         ValFunc(i_var, d_var, c_var);
          


Figure 6. Somewhere in Memory

The declaration of the function looks like this:

      void ValFunc(int arg1, double arg2, char arg3)
      {
         // Just print the values
         cout << arg1 << endl; // print i_var
         cout << arg2 << endl; // print d_var
         cout << arg3 << endl; // print c_var
      }

When you call the function, somewhere else in memory another set of variables (the arguments to the function) will be created and the values which were passed into the function copied into those variables. This is illustrated in figure 7.

          


Figure 7. Somewhere else in Memory



To Call By Reference in C we need to pass the addresses of variables as arguments to a function. If you execute the following code in main() then call the function RefFunc() you will be "calling by reference." The arrangement of memory accessed by main() is illustrated in figure 8

         int i_var;
         double d_var;
         char c_var;
         int *i_ptr;
         double *d_ptr;
         char *c_ptr;
         i_var = 16;
         d_var = 3.14159;
         c_var = 'a';
         i_ptr = &i_var;
         d_ptr = &d_var;
         c_ptr = &c_var;

Now you can call the function RefFunc() in either of two ways. Both, calls pass the addresses of the variables i_var, d_var, and c_var to the function. In the first call the actual addresses of the variables are passed. In the second call the addresses stored in the pointers are passed.


         RefFunc(&i_var, &d_var, &c_var);

         RefFunc(i_ptr, d_ptr, c_ptr);

          


Figure 8. Somewhere in Memory

The declaration of the function looks like this:

   void RefFunc(int *arg1, double *arg2, char *arg3)
      {
         // Just print the values in the variables pointed to
         cout << *arg1 << endl; // print i_var
         cout << *arg2 << endl; // print d_var
         cout << *arg3 << endl; // print c_var
      }

When you call the function, somewhere else in memory another set of variables (the arguments to the function) will be created and the values which were passed into the function copied into those variables. In this case the "values" being stored are addresses and they are being stored in pointer variables. This is illustrated in figure 9.

          


Figure 9. Somewhere else in Memory

While you have to "fake" the Call by Reference in C by passing in to the functions pointers to variables, in C++ you can create an actual Call by Reference function. The declaration of the function looks like this:

void RefFuncCPP(int& arg1, double& arg2, char& arg3)
      {
         // Just print the values in the variables referenc ed
         cout << arg1 << endl; // print i_var
         cout << arg2 << endl; // print d_var
         cout << arg3 << endl; // print c_var
      }

Using the variables declared above this function can be called with this statement:

RefFuncCPP(i_var, d_var, c_var);

When you call the function the arguments arg1, arg2, and arg3 actually become "aliases" for the variable names so values can be placed directly into the function arguments like this:

arg1 = 32; // store 32 in i_var
arg2 = 2.5; // store 2.5 in d_var
arg3 = 'z'; // store 'z' in c_var
   



Pointer Arithmetic

You can also perform some arithmetic manipulation of pointers. Consider the following code:


	int *ptr;	/* Declare a pointer to an integer */
	int tab[5] = {50, 25, 10, 5, 1};	/* Declare and initialize an array of 5 ints */

	ptr = tab; /* Set the pointer pointing to the first int in the array */

	printf("%d", *ptr);	/* Print the value stored in the first integer of the array.*/
	printf("%d", *(ptr+1));	/* Print the value stored in the second integer of the array.*/

	/* Print all integers in the array */
	for(i=0; i<5; i++)
		printf("%d", *(ptr+i));

	ptr++; /* Increment ptr to point to the next integer in the tab array */



For more on pointers and data structures see the page on data structures.