Published by
Nov 12, 2011 (last update: Nov 12, 2011)

Pointers

Score: 4.0/5 (264 votes)
*****
You are familiar with an int? An int is an object that takes up an amount of memory (commonly 4 bytes, sometimes 8) and stores a single numerical value.

A pointer looks exactly the same as that. It takes up the same amount of memory as an int, and stores a single numerical value. All pointers look the same, whether they are pointers to int or pointers to double or whatever. They take up the same space and store a single numerical value.

With me so far?

Now, you know how to create an int? Like this:

 
int a;


You create a pointer exactly the same way;

1
2
int* a; // This create a pointer to an int
double* b; // This creates a pointer to a double  


Like any object, you have to create a pointer before you can use it.

You can create a pointer to any kind of object you can think of using this syntax, but don't be fooled - the pointers all take up 4 (sometimes 8) bytes and store a single numerical value.

You know that if you create an int without assigning it a value, it'll be just some garbage value? Just whatever happens to be in the memory at the time.

 
int b; // This int could have any value at all  


Same thing with pointers

 
int* b; // This int pointer could have any value at all  


Of course, the same rules for naming things apply, so this:

1
2
int a;
int* a;


would refuse to compile, as I have two objects with the same name.

So far, so simple, yes?

There is one special thing a pointer can do. You recall that the pointer stores a single numerical value? I can carry out an operation called "dereferencing" on the pointer, and that simply means I get back whatever object is in the memory location of that single numerical value. So, if my pointer stores the value 0x22334455 (I've used hex here, but it's just a number), when I dereference the pointer, I get back whatever is at memory location 0x22334455. We sometimes say that the pointer is pointing at the object in memory location 0x22334455.

How do we know what numerical value to put into a pointer (or, put another way, how do we know what the memory address of an object is, so that we can store than memory address in the pointer)? Fortunately, there exists an operator "&" which will return the address of any object (i.e. the numerical value of the place in memory where that object is).

1
2
int a; // an integer
&a; // the address of that integer 


So here's how we could use that:

1
2
int* p; // a new pointer, with some garbage value - could be pointing anywhere
p = &a; // now we've stored the address of a in the pointer, so now the pointer is pointing at the object a  


There are circumstances where you might need to manually enter the address location yourself, and you can do that. Say you knew for sure that a value of interest was at address location 0x2323FFFF. You can manually set it, and we'll cover that a bit later.

If all pointers are the same size and store a single numerical value, why do we have int* and double* and string* and all the rest? Because we need to know what kind of object we will get back when we dereference them. That's all. If we were being awkward, we could make an int*, and then force it to point to a double, because the pointer just stores a number - it does not store anything about what kind of thing it's pointing to. That's a bit more advanced, though.

We do this dereferencing operation with the "*". So, if I had a pointer called MrPointer, and I wanted to get what it was pointing at, I would do this:

 
*MrPointer;


Let's say it points at an int. I could do this:

 
int someInt = *MrPointer;


or I can just use it in an expression

 
int anotherInt = 4 + *MrPointer;


I'll expand.
1
2
3
4
5
6
7
8
9
10
11
12
int a = 7; // Make an int, give it the value 7
int* MrPointer; // Make a pointer. Don't give it a value - it's got some garbage value
            //      if I try to use it, I deserve everything I get!

MrPointer = &a; // Now, MrPointer contains the memory address of the int a

*MrPointer; // This means "get me the int at the memory address you're pointing to" - we know the value
          //    of that int will be 7. I'm not doing anything with it, so this is a bit of a waste of code!

int b = *MrPointer; // Now I've made a new int, and set it to the value of whatever MrPointer points at. 
                   //    We know MrPointer holds the memory address of a, so the value returned is 7,
                   //     so the value of b will be 7.  


So, on to pointer arithmetic.

You are happy with regular arithmetic? Let's recap with an int.

1
2
3
int a = 7;
a++; // Now, a holds the value 8
a = a+4; // Now, a holds the value 12  


Pretty simple. What about pointers? First, take a step back and recall what pointers are all about. The idea is that they hold a memory address, and that memory address is the address of some object somewhere in memory.

 
int* b = &a; // the pointer b holds the memory address of our int a, from above  


So b will have some value, lets say it's 0x22334455.

Pointer arithmetic is the arithmetic of adding (and subtracting) values from a pointer. Recall that a pointer holds a memory address, so pointer arithmetic is the arithmetic of adding (and subtracting) values from a memory address. If we add the value 1 to a pointer, we don't mean "add 1 to that memory address you're holding". We mean "add enough to your memory to point at the next object along".

An int is commonly 4 bytes. If the address of b is 0x22334455, then the int will be occupying the bytes 0x22334455 and 0x22334456 an 0x22334457 an 0x22334458 (on some systems, it will be 0x22334455 and 0x22334454 and 0x22334453 and 0x22334452 - doesn't matter which, as your compiler will keep track of all that sort of thing for you and will adjust the pointer correctly). So if we have a pointer to an int, like b above, when we add 1 to that pinter, the memory address will change by 4 so that it now holds the memory address of the next int along in memory. It is up to us to make sure that there actually is an int there! The compiler will trust us to do it right.

So, imagine we had two int values next to each other in memory. We could have done this by making an array

1
2
int c[2]; // This will make two int values in memory, right next to each other
int* p = &(c[0]); // This makes a pointer p, and gives it the value of the address of c[0], which is the first int  


Note that this would also work;

1
2
int*p = c; // This makes a pointer p, and gives it the value of the address of c, which is itself a pointer,
            // pointing at the start of the array.  


Now, let's add 1 to the pointer.

 
p = p+1; // Now, the pointer holds the value of the next int along, c[1].  


Pretty clever, eh? When you add 1 to a pointer, the value it holds does not change by one; it changes by exactly the right amount so that it will point to the next object along. It knows how far that is because when you made it, you specified what kind of object it will point to. If it is an int pointer, the value it holds will change by 4 (sometimes 8) bytes. If it points to some crazy object that is 40 bytes long, the value will change by 40 bytes. You don't need to worry about how much the memory address actually changes by; you can trust the compiler to handle it for you.

If you added 2 to a pointer, the value of the pointer would change exactly the right amount to point at the object 2 along, and so on. If you subtract 1 from a pointer, it will point to the previous object along in memory.

So, check this:

1
2
3
4
5
6
7
double x[10]; // an array of 10 doubles
double* p = x; // p is a pointer, now pointing at the first double
for (int i=0;i<10;i++)
{
  std::cout << *p << std::endl;  // output the double value
  p++; // make the pointer point to the next one
}


This code outputs the (garbage) values of an array of doubles, and demonstrates pointer arithmetic.

As you'll realise, this opens itself to all sorts of neat stuff. How about if you had an int, and just for the fun of it, you wanted to look at each byte individually? A char is by definition one byte long, so if you had a char pointer, and made it point at that int, and then you added one to the char pointer, you'd be looking at the very next byte - inside the int!

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main()
{

  int a = 34;
  char* p = (char*)&a; // Make a char*, and force the compiler to treat the address of a as a char pointer.
  for (int i =0;i<4;i++)
    {std::cout << (int)(*p) << std::endl; p++;} // Cycle through the 4 bytes of the int, outputting each byte as if it were an int
  return 0;
}


This one is a tiny bit more interesting

1
2
3
4
5
6
7
8
9
#include <iostream>
int main()
{
  int a = 256;
  char* p = (char*)&a; // Make a char*, and force the compiler to treat the address of a as a char pointer.
  for (int i =0;i<4;i++)
    {std::cout << (int)(*p) << std::endl; p++;} // Cycle through the 4 bytes of the int, outputting each byte as if it were an int
  return 0;
}


You can get an insight into exactly what numbers are in the memory and you should be able to work out how the number 256 is being represented using the 4 bytes.

As you could guess, we could do lots of crazy stuff with this kind of power to directly interfere with individual bytes. Welcome to the low-level power of C and C++!


The difference uses of the "*" when dealing with pointers
The * has two meanings.


Let's cover the first meaning:
1
2
int b; // Make a new object. The new object is to be an int.
int* a; // Make a new object. The new object is to be an int-pointer  


When we create objects, the "*" is just part of the description of what kind of object we want to make. Once we've made the object, we just use the name of the object.

Here's how we pass the object b that we just made:

 
someFunction(b);


We don't need to specify that b is an int when we actually use it.

So here's how we pass the pointer we just made:

 
someOtherFunction(a);


We don't need to specify that a is an int-pointer when we actually use it.

The other use of "*" is as the dereference operator. We use it on a pointer when we don't want the actual pointer; we want what it is pointing at.

1
2
int* c; // Here, "*" is part of the kind of object we want to make
*c; // Here, "*" means dereference the pointer and give us what it points at  


They are two different uses, with two different meanings. If you see int* or double* or string* or any other such <object-type>* it is describing a kind of object. If you see *pointer, it means dereference.

You will see the "describing a kind of object" use in making pointers, and in specifying what kind of objects a function takes (and returns), as in this example function prototype:

 
void DealDamage(int *EnemyHpPointer, int damage, string EnemyName);


DealDamage takes a pointer to an int, an int, and a string. It could be used like this:

1
2
3
4
5
6
int* somePointer;
int someValue;
string someString;

// and then, after these have been given values, use the function
DealDamage(somePointer, someValue, someString);