Understanding Pointers (with Lego) Part 1

Ahhh pointers.

I’ve been wanting to write a series of posts on pointers for a long time. When I’ve finished, I’ll upload a complete PDF eGuide. In the meantime, enjoy 🙂

Pointers.

A concept that some programmers avoid through fear, others abuse through ignorance, and yet others manage to use with grace and efficiency.

Every programmer who does more than dabble in C and C++ needs to understand this subject. Properly.

This is Part 1 of a multi-part series, and there will be a lovely quiz at the end, which I expect you will all get 100% on after reading my posts, right?

Let’s crack on.

Lesson 1: The Memory and The Pointer

If you could make yourself really small, you could dive into your computer and take a look at its memory banks (i.e. its RAM).

It might look something like this:

memory

 

Millions and millions of little blocks, just waiting to be filled with information.

For the purpose of these tutorials I am making a couple of assumptions, which I explain in more detail below:

  1. We are dealing with a 32 bit machine.
  2. Our machine has a word size of 4 bytes.

So, let’s learn a little bit about our computer’s memory.

Each square of Lego that you see in the picture above represents one byte. Each one of those bytes has its own personal memory address. You can look at the contents of any byte (any Lego brick) in this picture if you know where it lives.

OK so far?

Good.

The reason the squares above are set up in groups of different colours is because computers have a set “word” size that they like to use. Computers like to handle complete words when they are performing instructions. Any instruction can handle the data in one complete word, but no more.

Our word size is 4 bytes, or 4 Lego blocks.

Words can’t actually be seen in memory, I have used colour simply to make it easier for the eye to see where the computer’s natural instruction boundaries lie.

1 Lego brick = 1 byte
4 Lego bricks = 1 word = 4 bytes

I mentioned that this machine was a 32 bit machine. This is because the total number of bits in each word is 32.

1 Lego brick = 1 byte = 8 bits
4 Lego bricks = 4 bytes = 32 bits

So our computer can carry out instructions containing up to 32 bits of data. Hence it is a 32 bit machine.

Let’s go back to the most important point here. One Lego block = 1 byte, and each byte has its own personal memory address. The next picture shows our Lego bytes with addresses on them. Addresses are just consecutive numbers and they are usually shown in hexadecimal format:

 

addresses

Each byte, in each word, has its own personal address

 

OK, enough about memory. Time to talk about pointers.

 

What is a pointer?

A pointer is a variable.

There is nothing magical about it. It is just like any other variable in that it has its own location in memory and it holds a piece of data.

The difference – and this is the crucial part – is that the data that a pointer variable stores is, in fact, the memory address of another variable.

So, instead of storing a number, or a letter, or any other piece of data that a variable might contain, a pointer stores an address.

Let’s see what a pointer really looks like:

 

A pointer holds the address of another variable

A pointer holds the address of another variable

 

Here’s our pointer, which itself lives at 0x31.

It is holding the address of 0x39.

 

Why is it taking up 4 bytes?

Now, this is a great question. And it takes us back to memory again.

Our pointer covers 4 Lego bricks (4 bytes) so that the computer can access every byte of memory in 4GB of RAM.

 

Huh?

Remember I talked about how our computer uses words in its instructions? A word is four bytes, or 32 bits. It can’t handle data that doesn’t fit into this space.

What’s the greatest number that can fit into 32 bits?

I’ll tell you. It’s 4,294,967,295.

Or, in 32 bits, the computer can store addresses from:

0x00000000 (zero)
0x00000001 (one)
.
to
.
0xffffffff (4,294,967,295)

Because we address from zero, we have a total of 4,294,967,296 bytes that we can recognise.

And 4,294,967,296 bytes is the equivalent of 4 gigabytes.

Hence why 32 bit machines can only support 4GB of RAM.

So the pointer might just store 0x39 (like in the picture), or it might be storing 0xff45fde1, either way, it always uses 4 bytes of memory to keep this data safe for you.

 

OK, I get it, pointers need 4 bytes because the address range is 4 bytes. Can we please get back to pointers now?

Sure.

Our pointer is holding the address 0x39, but what lives at 0x39?

 

pointerToInt

The pointer holds address 0x39. Address 0x39 holds the number 10.

 

It’s the number 10.

PAY ATTENTION: THIS IS WHERE THE MAGIC HAPPENS.

We can use the pointer variable to see inside the variable whose address it contains.

If we ask our pointer (at 0x31), what 0x39 contains, it will tell us: 10.

And it will do so without us ever having to look at address 0x39 ourselves.

It’s like having web-cam access to a motorway we want to drive along. Is there a traffic jam? We can find out without actually going there, saving us time and effort 🙂

Is that a good metaphor?

Perhaps not.

Anyway, let’s have another look:

 

twoPointers

Two pointers, each holding an address.

 

Now we’ve got two pointers. Our original one lives at 0x31 and our new one lives at 0x2d. Both of them hold addresses: 0x39 and 0x3d respectively.

What are they pointing at?

Let’s look at the memory addresses:

 

TwoInts

Two ints stored at 0x39 (10) and 0x3d (5).

 

Our pointers are pointing at integers.

Again, we can use both of our pointers to tell us about the data stored at the relevant address, without having to go to that address ourselves.

Phew!

Now we’ve got a nice picture of what a pointer is and what a pointer is doing, let’s use some real code to illustrate it.

 

In code

First of all, here’s the code that puts to work the theory above:

 

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

int main()
{
	// First pointer - declare a pointer, declare an int, then assign
	// the address of the int to the pointer
	int *p1;
	int num1 = 10;
	p1 = &num1;

	// Second pointer - declare an int, then declare a pointer
	// and assign the address of the int in one line
	int num2 = 5;
	int *p2 = &num2;

	// Let's take a look at our pointers

	printf("p1 (at address 0x%x) points to value: %d at address: 0x%x\n", &p1, *p1, p1);
	printf("p2 (at address 0x%x) points to value: %d at address: 0x%x\n", &p2, *p2, p2);

	return EXIT_SUCCESS;
}

 

There is a screenshot below showing the output of this program, but first let’s go through it line by line.

Line 8

This is how we declare a pointer. Note that a pointer, just like every other variable, has to have a type (I’ll talk more about types in Part 2). Here we are declaring a pointer to an int. The ‘*’ in this line doesn’t do anything, it is a reminder that the variable you are declaring is a pointer.

Line 9

Declaration of an int. You don’t need me to explain this, right?

Line 10

In this line we are assigning the address of the int to the pointer. You know how to take the address of a variable? That’s right. You pop the reference operator ‘&’ in front of it.

Note that we’ve dropped the ‘*’ from the pointer. We’re using the pointer variable as a normal variable now, so we can just call it by its usual name.

Line 14

Another int declaration. Nice and simple.

Line 15

Instead of declaring a pointer and then assigning the address, we’re doing it all in one line here. This is a pointer declaration followed by assignment of the address of num2.

Note that as this is a declaration we’re using the ‘*’ to tell ourselves (and the compiler) we want this variable to be a pointer.

Line 19 + 20

Here we can see the basics of how pointers work.

This is where ALL the information you’ve learnt so far is crystallised. Forever and ever, never to be forgotten.

Take a careful look at the first printf statement. It is printing out 3 variables:

&p1
*p1
p1

Can you guess what each of these is?

 &p1
We’re using the reference operator here – what does that do? That’s right, it gives you the address of a variable. What’s the variable? It’s a pointer. So we are printing out the actual address of a pointer. In honesty, there’s not much call for you to do this, but it’s an important concept because it shows you that a pointer really is a variable in memory, with its own address, just like any other.

*p1
Ah ha! We’re using the ‘*’ operator again. This clearly isn’t a declaration, so what’s going on? This is how pointers do their magic. Putting a * in front of the pointer name gives you the contents of the address it points to. This is called dereferencing.

 p1
And here is the plain old pointer variable. If we don’t reference it for the address (with &), and we don’t dereference it for the value of what it points at (with *). What do we get? We get the address it contains.

 

output

 

 

Pointer Concepts

Since I love repeating myself, let’s just sum that up one more time.

  • Dereference a pointer to get the value of what it points at: *p1

  • Use the plain, old pointer name to see the address it stores: p1

  • Reference a pointer to get the pointers address: &p1

 

In Summary

We’ve covered a LOT here. Way more than I intended, but pointers are such a great subject I just had to keep on writing, and now you’ve spent ages reading all this and your brain is feeling sore. Sorry.

OK, so in summary:

  • Memory (RAM) is addressed in 1 byte blocks.
  • 4 bytes make a word, which is the maximum size that the computer can handle when performing instructions (on a 32 bit machine).
  • A pointer is a variable.
  • A pointer stores the address of another variable.
  • Pointer take up 4 bytes of space (on a 32 bit machine), because addresses are represented by a 32 bit number from 0 to 4,294,967,295.
  • Declare a pointer by using * in front of the pointer type (we have only used ints so far – see Part 2 for more).
  • Assign an address to a pointer using the reference operator in front of your data type: p = &num
  • Print or access the address a pointer holds by using just the pointer variable name: p
  • Print or access the value of the pointed-to variable by dereferencing the pointer: *p
  • Print or access the address of the actual pointer by using the reference operator: &p

 

Coming up:

In Part 2, I’ll be talking about different kinds of pointer: int/char/void, I’ll talk about arrays, and if I have space, the super-duper subject of pointer arithmetic, which I love. Otherwise that will come in Part 3, along with lots of other pointer goodies.

See you soon!