C/Pointers with words
Having been criticized by my colleagues, I decided to add some words to my Pointers page.
A Variable
Before we begin, this page assumes an x86 system (32-bit) - which means that addresses and integers are 32-bit / 4 bytes.
If we start by thinking about a normal variable, i
, of type int
.
An int
is just a block of 4 bytes that the compiler promises it will find memory for.
In reality this comes from the stack, but that is a detail for another time.
Let's declare i
and assign 0xDEADBEEF
to it.
int i; i = 0xDEADBEEF;
If you were to inspect the memory for this process, and represent it visually, we might find the following (the colouring will become apparent later).
Address | Value | Note |
---|---|---|
... | ||
0xBF893E8F |
0xDE |
i MSB
|
0xBF893E8E |
0xAD |
i
|
0xBF893E8D |
0xBE |
i
|
0xBF893E8C |
0xEF |
i LSB
|
... |
From this memory map, we can see that the variable i
is stored at address 0xBF893E8C
, and because the memory is byte addressable, i
uses 4 'slots' - 0xDE
, 0xAD
, 0xBE
, 0xEF
.
A Pointer
Now let's consider a pointer.
They are just like normal variables, but they have a special meaning to the compiler.
int *p; p = 0xDEADBEEF;
The code above gives exactly the same result, and assuming that p
is located at the sample place in memory that i
was, the previous memory map is still valid.
But when we come to 'de-reference' p
we are going to have a problem.
The term 'de-referencing' is the act of following a pointer, to access the variable to which it points. This can be done in two ways:
*p /* this is usually used in a situation similar to this, when 'p' points at a single variable */ p[0] /* this is usually used when 'p' points to an array of variables */
The map below shows the value of p
as well as the memory that it points to.
As you can see, this is no longer a simple variable, because its value is used to locate some other memory.
In this case, the memory at 0xDEADBEEF
is not allocated to anything, and accessing it will cause a segfault (your program will crash).
Address | Value | Valid? | Note |
---|---|---|---|
... | |||
0xDEADBEF2 |
?? |
No | ?
|
0xDEADBEF1 |
?? |
No | ?
|
0xDEADBEF0 |
?? |
No | ?
|
0xDEADBEEF |
?? |
No | ?
|
... | |||
0xBF893E8F |
0xDE |
Yes | p MSB
|
0xBF893E8E |
0xAD |
Yes | p
|
0xBF893E8D |
0xBE |
Yes | p
|
0xBF893E8C |
0xEF |
Yes | p LSB
|
... |
Let's make something useful, instead of something that will crash.
Consider the following code:
int i; /* our variable */ int *p; /* our pointer */ p = &i; /* assign the address of 'i' to 'p' */ *p = 0x12345678; /* effectively assigns 0x12345678 to 'i' */
In this snippet, we make p
point at i
, and then store 0x12345678
in the memory that p
points to.
This effectively stores 0x12345678
in i
could result in the following memory map.
Address | Value | Valid? | Note |
---|---|---|---|
... | |||
0xBF893E8F |
0x12 |
Yes | i MSB
|
0xBF893E8E |
0x34 |
Yes | i
|
0xBF893E8D |
0x56 |
Yes | i
|
0xBF893E8C |
0x78 |
Yes | i LSB
|
0xBF893E8B |
0xBF |
Yes | p MSB
|
0xBF893E8B |
0x89 |
Yes | p
|
0xBF893E8B |
0x3E |
Yes | p
|
0xBF893E8B |
0x8C |
Yes | p LSB
|
... |