Pointers

A variable in a C program is a data item that has a name, and a value that can vary. When the program runs, it allocates a block of memory in which to store the variable, the size of which depends on the type of the variable. An integer variable, for example, is allocated 4 bytes on most 32-bit computers. As well as a name and a value, therefore, the variable has an address in memory.

A pointer is a special kind of variable that holds the address of another variable, and is so called because it can be used to "point" to that variable. The size of a pointer variable will depend on the number of bits used by a particular computer system to store memory addresses. The bigger the memory, the more bits are needed. Consider the following declaration:

int *ptr;

The name of the variable is ptr. The asterisk (*) tells the compiler it is a pointer variable, and therefore requires sufficient space in memory to store an address. The use of the keyword int indicates that the pointer will be used to store the address of an integer variable. Initially, the pointer does not have a value, i.e. it is a null pointer. We can test for a null pointer, e.g.

if (ptr==NULL) <do something>;

To make the pointer variable ptr point to an existing integer variable, we could copy the address of the existing variable into ptr as follows:

int i = 10;
ptr = &i;

The ampersand (&) operator retrieves the address of i, and assigns its value to ptr.

The process of getting the value of a variable pointed to by a pointer variable is called de-referencing the pointer, and is achieved using the dereferencing operator (*), which is used as follows:

int *ptr;
int i, j;

*ptr = &i;
i = 10;
j = *ptr;

The first statement declares a pointer to an integer variable, while the second declares two integer variable (i and j). The third statement assigns the address of i to ptr, and the fourth statement assigns the value 10 to i. The last statement assigns the value of the variable pointed to by ptr to j, and since ptr points to i, the value assigned to j is 10. We could write the following statement to print the integer value stored in the variable pointed to by ptr:

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


Pointers and arrays

The type of variable that a pointer variable refers to is important for two reasons. First, the compiler needs to know what type of variable it is dealing with so that it knows how many bytes of data to copy to (or from) that location. It is even more important, however, when the pointer points to an array of variables. For example, if the pointer variable ptr points to the first integer variable in an array of 10 integer variables, think about the implications of writing:

ptr = ptr + 1; // (or ptr++;)

Because the compiler knows that ptr is a pointer, and that it points to a variable of type int, the above statement will assign a value to ptr that points to an address in memory that is four bytes higher than the address it currently points to. If we assume that the original value of ptr was the address in memory of the first item in an array of integers, then the new value will be address of the second item in the array (remember that array variables are stored in contiguous memory locations).

Incrementing or decrementing a pointer value will increment or decrement the address assigned to the pointer by an amount that reflects the size of the variable data type referred to (in bytes). This is true even when the data type in question is a user-defined data type such as a structure. Incrementing or decrementing pointer variables in order to move backwards and forwards through an array is sometimes called pointer arithmetic. Consider the following short program:

// Example program 1
// Program to compare the use of array indexes and pointers

#include <stdio.h>
#include <conio.h>

void main()
{
  int my_array[] = {1, 3, 5, 7, 9, 20, 16, 10, 27, 31};
  int *ptr;
  int i;

  ptr = &my_array[0];
  for(i=0; i<10; i++)
  {
    printf("my_array[%d] = %d ", i, my_array[i]);
    printf("ptr + %d = %d\n", i, *(ptr + i));
  }
  printf("\n\nPress a key to continue . . . ");
  getch();
}

If you type in this program and run it, you will see that the two printf() statements inside the for loop produce exactly the same output. The first statement, however, does this by indexing into the array, whereas the second de-references successive pointer values. The output from example program 1 is shown below.

The output from example program 1

The output from example program 1

Note that where we wrote:

ptr = &my_array[0];

we could also have writen:

ptr = my_array;

Effectively, the name of an array variable can also be considered to be a pointer. Bear in mind, however, that whilst the value of my_array can be assigned to ptr, the reverse is not true, since ptr is a variable and my_array is a constant.


Pointers and strings

In C a string is an array of type char, terminated with a null character (written as '\0'). Consider, for example:

char my_string[20];

my_string[0] = 'F'
my_string[1] = 'r'
my_string[2] = 'e'
my_string[3] = 'd'
my_string[4] = '\0'

Although we would not build a string like this, the result is still a string, because it is an array of characters terminated with a null character. There are two other ways in which we can achieve the same result:

char my_string[20] = {'F', 'r', 'e', 'd', '\0'};

or

char my_string[20] = "Fred";

When double quotes are used, the null character ('\0') is automatically appended to the end of the string. In all of these examples, the compiler allocates a contiguous block of memory 20 bytes long (enough to hold 20 characters) and initialises the array so that the first four characters are:

F r e d \0

An array of characters can be manipulated using pointers in exactly the same way that an array of integers can. The short program below uses the memcpy() function (defined in string.h) to extract two separate substrings from the string variable input_string. The function works by copying a specified number of characters from one string into another.

The arguments passed to memcpy() are the target string, a pointer to the start of the required substring (expressed as an offset) and the number of characters to copy. The example program below program uses pointer arithmetic, together with the memcpy() function, to extract the date and time elements from one string, and insert them into two other strings.

// Example program 2
// program that uses pointer arithmetic to extract substrings

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>

void main()
{
  char nowStr[42] = "Current date & time: 04-Jul-2008 22:43:53";
  char dateStr[12];
  char timeStr[9];

  memcpy(dateStr, nowStr+21, 11);
  memcpy(timeStr, nowStr+33, 9);
  printf("%s\n\n", nowStr);
  printf("\nThe date alone is: %s\n\n", dateStr);
  printf("\nThe time alone is: %s\n\n", timeStr);
  printf("\n\nPress any key to exit . . . ");
  getch();
}

The output from example program 2 is shown below.

The output from example program 2

The output from example program 2