The & operator takes a variable and gives back its address. So for example, if we have a variable int steve, the expression &steve is the address of steve. This address can be stored in a pointer.

int steve; int *ptr; steve = 5; ptr = &steve;
In the above code, we declare two variables, steve, an integer, and ptr, a ptr to an integer. steve is then given the integer value 5. On the next line, ptr = &steve tells the computer, "take the address of the variable steve and store that address into the variable ptr". Pretty straightforward, right?

Figure %: Pointer ptr points to integer steve

The address stored in the pointer is like any other value in a variable. We can assign it to another variable:

int steve; int *ptr; int *ptr2; steve = 5; ptr = &steve; ptr2 = ptr;
Figure %: Copying an address to another pointer
We can check to see if two pointers contain the same address:
int steve; int *ptr; int *ptr2; steve = 5; ptr = &steve; ptr2 = &steve; if (ptr == ptr2) printf("Equal\n");
We can even perform simple arithmetic operations on them, like subtraction. More on that in a later section.

Using the information in a pointer

Now that we have the ability to create pointers and to put addresses into them, what can we do with them? Remember that a pointer's job in life is to hold the address of a location in memory. Wouldn't it be great if we could then take that address and find out what it contains? Well we can; that's the whole idea.

To find out what the memory at an address holds, we use the * operator. As we've seen, the * operator has multiple meanings in C. It can be the multiplication operator. It can be used to declare a pointer. It can also be used to dereference a pointer.

Dereference? Yes. To dereference a pointer means to take the address contained in the pointer variable, and go find whatever data resides at that address. You might find it helpful to think of an analogy. Think of the phone book as a huge array of pointers. Each entry in the phonebook contains the address of a person's house. To find out who lives in that house, you get in your car, drive over there, knock on the door, and see who answers. That process of driving to the persons house and seeing who was inside is like dereferencing a pointer.

To dereference a pointer, we use the asterisk. By putting the asterisk operator in front of a pointer, we're telling the computer to go fetch the memory addressed by that pointer.

int steve; int *ptr; steve = 5; ptr = &steve; printf("%d\n", *ptr);
In the above code, we again declare two variables, an integer and a pointer to an integer, then store the value 5 into steve and the address of steve into ptr. The pointer ptr now points to the variable steve. Therefore, *ptr is equivalent to the variable steve and can be used synonymously. If we ask for the value of *ptr, we're going to get whatever value steve holds. If we store something into *ptr, we're storing that something into steve.

Figure %: Dereferencing a pointer

Something to be very careful of here. When you first declare a pointer, as mentioned above, it doesn't point to anything meaningful; like all variables when they are first declared, it contains garbage. When you declare a variable, the computer goes and sees what memory it has available and then assigns your program a small chunk of it for the variable. However, it doesn't clear out the memory at that location. Whatever was in that memory location before you were given the right to it is still there. This can lead to trouble if you're not careful.

Look at the following code:

int *ptr; *ptr = 5;
What did we just tell the computer to do? We declared a pointer variable, and then we immediately dereferenced it and stored the value 5. Do you see the problem here? We haven't initialized ptr, meaning that whatever it contained before we were given it is still there; in other words, it points to a random place in memory. We then tell the computer, "go to this random location in memory and try to store the value 5 there". Hopefully your computer's operating system is much smarter than this. That random place in memory could be anywhere: it could be memory being used by Microsoft Word, it could be memory being used by the operating system, it could be memory being used by the countdown timer for the nuclear warhead sitting in your backyard. The point is, you don't want to go modifying memory that doesn't belong to your program, and neither does your operating system. If you try to do something like this and the operating system sees that what you're attempting to do may be harmful to itself or other programs, it will stop you the only way it can, by killing your program. This is commonly referred to as crashing, or causing a segmentation fault. The operating system attempts to protect the rest of the system by shutting down your program if your program behaves in an unacceptable way.

Figure %: An uninitialized pointer initially points to a random location

Now that we've seen the dereferencing operator, the declaration of pointers might make a little more sense. Instead of thinking of int *ptr as a pointer to an integer, we can imagine that "*ptris an integer". Of course, this method of thinking about pointers has some drawbacks, mostly linked with the memory problem described above. When you first declare int *ptr, it most likely doesn't point to anything valid, but if you think of int *ptr as declaring *ptr as an integer, you might think you can use it just like any other integer. Unfortunately you can't because, again, it most likely doesn't point to anything legal.

Pointers to structures: the -> operator

Let's say we have the following code:

typedef struct _person_t { char name[100]; int age; } person_t; person_t steve; person_t *ptr = &steve;
We've created a data type called person_t that holds a name and an age, we've created a variable of that type, steve, and we've created a pointer variable ptr that can point to a person_t. As described above, we can dereference ptr just like any other pointer variable by placing an asterisk in front of the name, as in *ptr. *ptr can be used just like steve.

Remember that with structures (and classes in C++) we use the . operator to get at the fields contained within the complex type. So for example, to access the steve variable's age field we would write steve.age. We can do the same thing with the *ptr, for example (*ptr).age. Why, you might be asking, do I have those parentheses there? The answer is that the . operator binds tighter than the * operator, meaning that this is equivalent to *(ptr.age). The computer will first try to get the age field of the ptr variable, and then attempt to dereference it. As such, we need to put in the parenthesis ourselves to force the computer to do what we want.

This can get tedious, especially considering the frequency with which pointers and structures are used in combination. To make our lives easier, C provides another operator, the -> operator, which can be thought of as the "dereference this pointer and get a specific field" operator. Instead of writing (*ptr).age, we can just write ptr->age.

Pointing to... NOTHING

Often it is useful to have special values to indicate that a variable is not valid. For pointers, C/C++ gives us a special value to use, the NULL value, that says just that. Programmers use the value NULL to signify that a pointer does not contain a valid address, that it doesn't point to anything useful. A particularly useful feature of NULL is that it's guaranteed to be interpreted as "false" in if constructs:

int* steve = NULL; int x; ... /* Only dereference steve it it's not NULL. */ if(steve) { x = *steve; }

All the basic tools

You now possess all of the basic basic knowledge needed to use pointers. However, why we'd want to use pointers may still seem a mystery. As you progress through the following sections of this guide, you'll see how useful pointers really are.