« Older: Blocks and Memory « Older
Newer: Recursion »Newer »
C Pointers
I’ve written this post because I see a lot of confusion on the internet about C pointers and it’s almost always a misunderstanding of the basic syntax. So here’s a post explaining the syntax.
For the whole of this post we’re going to ignore
the case where *
means multiply (when it is placed
between two number types).
Random Access Memory (RAM)
To understand what’s going on with pointers you first need to know about how memory works on your computer. If you know this already skip onto the next section.
Your computer has some very fast memory used for running programs, this is called RAM (Random Access Memory). This memory is addressed; this is done by giving each byte a number. The computer can then ask the RAM chip for the data at that address (number) and it’ll get the data. This is why 32 bit systems can only support 4 GBs of RAM; a 32 bit system has an address size 32 bits long, i.e. it only has 2^32 numbers with which to address the RAM (2^32 bytes = 4 Gigabytes).
Addresses
When we use the word address we mean the location (byte number) in RAM of the start of the data we are interested in.
When you create a variable in your program that variable is
put into the RAM for the lifetime of the current scope (within
the {...}
which the variable is declared). This means that
your variable has an address, you can get the address using the
&
symbol. The following code prints the address of variable a
:
%#llx
prints up to a 64 bit number in hexidecimal integer
with 0x
preceeding it. The top print will give you a type
warning because the compiler knows that &a
has the type of
an address at which there is an integer, not type long long int
(which is the type we have asked it to print) but as an address
is just a number it’ll work without any problems. %p
is used
to print addresses. If you run this code you will find that both
lines will print exactly the same thing, which is the
location/address in RAM of a
.
Pointers
The first thing to do is to introduce the jargon:
- A pointer: A variable that contains the address of some area of your RAM.
- Pointed at: The data at the address of a pointer
I.E. “The thing pointed at by a
”: a
is a pointer,
and we want to talk about the data at the address stored in a
)
All you need to know to understand pointers is that *
means
“the thing pointed at by”. Let’s look at some examples of this:
This means “a
is a variable of type int
”.
This means “the thing pointed to by a
is of type int
”.
- The thing pointed at by
a
is anint
. b
is anint
and equals10
.a
equals the address ofb
.c
is anint
and equals the thing pointed at bya
(theint
at the address stored ina
).
At the end of this c
will equal 10
.
This is nearly the same as above except the last line. The last line says:
The thing pointed at by a
equals 15
. a
points at the memory of
b
so if we printed the value of b
at the end of this we would
find that it had changed from 10
to 15
.
As we can see from the examples every time we see *
we can
read the thing pointed at by.
You can create and set a pointer in a single line like this:
The second line of this code reads “The thing pointed at by b
is an
int
, and b
equals the address of a
”. Note that in this case the
*
only applies to the declaration of b
and not to setting b
. i.e.
This code is equivalent to:
When using *
like we have above (both in declarations and when
placed in front of a variable) it works on the variable that comes
directly after it. So in this case:
*
changes a
, it does not change the type. This means that if
you write something like this:
The code above can be read as b
, and the thing pointed at by a
are int
s.
Note that it would seem more natural to write the statement above with
a
first as this is how we declared it, unfortunately this statement is
ambiguous in English, and would need to be read like this: (the thing
pointed at by a
) and b
are int
s, taking note of the brackets.
To get a
and b
to be pointers you need to write:
To put this in technical terms *
is a unary prefix operator. An operator
is a symbol that represents a specific action, unary means that it acts on
a single element, and prefix means that is comes directly before the
element.
You will see people write code like this int* a
, this makes
it look like *
is changing the type, i.e. changing the type
from int
to a pointer to an int
. This isn’t how C works and the compiler
will still read the code like this int *a
. Doing int* a
is as weird as
writing bool a = true; bool b =! a;
Note if you did this it would seem
like bool c =! a && b
would mean bool c = !(a && b)
.
What’s next
There is loads more to explore about pointers in more detail such as arrays and pointer arithmetic or types/typecasting (especially interesting in an Object Oriented situation such as Objective C or GTK).
Have fun.