7.0 Pointers

A pointer is a variable that holds the address to a variable. The address of a variable is where it is stored in the computer's memory. Pointers allow us to create dynamic data structures, handle variable parameters passed to functions, and they also give us an alternate way of working with arrays. A pointer can point to any type of variable that is available and is used just like any other type of variable. One thing many people are confused about when first learning pointers is that the pointer variable only contains a memory address, it does not contain the value of the variable that it is pointing to. Let's see some examples to explain this concept more clearly

Example Illustration

7.1.0 Pointer Basics

Pointers have many of the characteristics that a regular C variable has. This way we can declare and use them easily. To declare a pointer you must specify the data type that the pointer will point to and then the name of the pointer. Here is a quick example of a pointer to an integer.


int ip*;

Simple enough eh? Well one thing to keep in mind is that this pointer in not pointer to any data as of yet. Here is what we have done by declaring the pointer.
Example Illustration

As you can see this gives us a pointer that could pointer to any memory address, so we must be careful not to use the pointer until we actually point it to something valid. Otherwise we could modify memory from other programs, which will cause problems. So how do I assign it some valid memory then you may be asking yourself. Well, there are many different ways of assigning a memory address to a pointer. The most basic way was show to you in the first illustration of this chapter.

int i = 5;
ip = &i;

C can get the memory address of a variable by referencing it. The amperstand is used as the referencing operator and it is placing in front of the varialbe that you wish to reference. In the above example we referenced the integer variable i

ip = &i;

and then assigned that memory address to the pointer ip.

You may ask why I didn't assign the memory address as

*ip = &i;

This is because the C uses the asterik to dereference a variable, and to declare a pointer. Dereferencing gives us the contents of the memory address that is pointed to. So if we used *ip = &i that would put the memory address of i as the contents of the pointer ip. The first thing that is wrong with doing that is that the pointer is still uninitialized. And the second thing wrong with the statement is that it would put the memory address into ip's contents, the pointer would still point to nothing. When you are assigning a memory address to a pointer you will not want to put the dereferencing operator (*) in front of the name of the pointer, this means that you are going to work with the actual value of the pointer which should only be a memory address. When you need to change the contents of the variable that a pointer points to you would use the *. Here is an example followed by an illustration.

int i = 5;
int *ip;

ip = &i;	// Point ip to the memory address of variable i.
*ip = 10;	// Change the contents of the variable ip points to to 10.

Example Illustration

7.2.0 Pointers and Arrays - Pointer Arithmetic

Now that we have talked about basic pointers, let's move on to the next step. In the first section we discussed pointers to single variables. What happens when we create a pointer that points to an array? C has a very close link between pointers and arrays which may confuse the beginner, so we will spend a little time discussing their similarities and differences.

As you may recall an array is a group of cells of a specified length that is created in the computer's memory, we can access the different cells in the array by specifying the element number, or position, in the array. Looking at how the memory of an array is setup will help us understand how all this works. Let's say we wanted to create an array that holds five integers. First we would declare the array...


int a[5];

Now let's look at an illustration to explain how this array looks in the computer's memory...
Example Illustration

As you can see from the illustration the memory is in blocks the size of the element's data type, 4 bytes per integer. The memory blocks are set one by one beside each other. Now if we think back to what a pointer really is, a reference to a memory address, this brings up a very interesting concept. What would happen if we were to say, increment a pointer. Well, it would simply move to the next memory address according to the size of the data type the pointer points to. So if we had a pointer to the array a we would have a pointer to the first element of the array. If we increment the pointer it will point to the next element in the array, a[1].

To understand why we can do this with arrays we will need to take a closer look at how an array works. When you have created an array the first element in the array is referred to as a[0], the first memory address of the array. To assign a pointer to the array we can use the following declaration...

int a[10];
int *pa = &a[0];

Basically this assigns the memory address of a[0] the first element in array a to pointer of type int. Incrementing the pointer, pa++, moves it to the next block of memory which would be the second element of the array a, &a[1]. One thing you need to know about arrays in C is that they will always decay, or turn, into a pointer, so we could rewrite the assignment of the previous example as...

int a[10];
int *pa = a;

Since C will decay the array a into &a[0] for us, this is completely legal to do. Say that we wanted to declare a pointer to the fifth element in the array, we would do so like this...

int a[10];
int *pa = &a[4];

or...

int a[10];
int *pa = a+4;	// Converts to &a[0] + 4 or &a[4]

With all of this in mind you can do as I previously said about incrementing and decrementing pointers. This is called pointer arithmetic and it allows us to a pointer to reference any point in an array just by assigning the pointer a memory address inside of the array and then moving around by incrementing and decrementing the pointer. Here is an example with an illustration...

int a[5] = {1,2,3,4,5};
int *pa = a;
int i, total = 0;

for(i = 0; i < 5; i++)
{
	total = total + *pa;
	pa++;
}
printf("The total is %i", total);

Example Illustration

The main thing to keep in mind when using pointer arithmetic is that you increment and decrement only the pointer, not the the contents of the variable that the pointer point to.

int a[5] = {1,2,3,4,5};
int *pa = a;

pa = pa+3;	// Increments the pointer by 3

*pa = *pa+3	// WRONG: Increments the contents of the variable
		// 	  pointed to by pa by 3

In the first statement the pointer pa has moved 3 places in the array, now referring to a[3]. In the second statement, the contents of *pa, a[0], have 3 added to them and then are assigned to *pa, the value of a[0]. So now the values of the array look like this {4,2,3,4,5}. As you can see, these are two very different things... make sure you are really doing what you want by using the correct syntax!!

7.3.0 Pointers as function arguments

In the previous section we discussed how an array is decayed into a pointer by the compiler. So this means that when we pass an array to a function we are really passing a pointer to the first element of the array. Example:


char function(char a[]);

char function(char *a);

Both of these functions can be used interchangably because they are both just memory addresses, or pointers. When you see char a[] you may think that you will be passing the function an array, but as we discovered in the previous section all arrays decay into pointers. So when you make the function call function(a), C will decay the array a into a pointer to the first element of the array, &a[0]. This is how we can declare the function with the argument as a pointer instead of an array. Note that you can use either array indexing or pointer arithmetic inside of the function not matter how the array is passed.

void function (char a[], int len)
{
	int k;

	for(k = 0; k < len; k++)
	{
		printf("%c", *a);
		a++;
	}
}

void function (char a[], int len)
{
	int k;
	
	for(k = 0; k < len; k++)
	{
		printf("%c", a[k]);
	}
}

void function (char a*, int len)
{
	int k;

	for(k = 0; k < len; k++)
	{
		printf("%c", a[k]);
	}
}

void function (char *a, int len)
{
	int k;

	for(k = 0; k < len; k++)
	{
		printf("%c", *a);
		a++;
	}
}

All these functions will do the same thing except the do it using several different methods, keep in mind that sticking to pointers and pointer arithmetic generally results in faster executing code.

Take Chapter Quiz