Memory, addresses and pointers
Memory is a single very long row of numbered boxes. Each box holds a number. The number of a box is its memory address, and that is the whole of the model: there is no hierarchy, no names, and nothing inside the machine that knows a box is called a.
Below, a short program runs a line at a time and memory is drawn after every one. Watch where each new value lands: one box after the last, 8 bytes along, every time.
Then press pointer. One line changes, and the number put into the new box is not a quantity but an address. That is the entire idea, and everything difficult about pointers is downstream of it.
1let a = 422// ...more variables...34let copy = a // takes the value5copy = 99 // writes to copy67print(a) // still 42
Memory
one box per value, with the address it lives at
Memory is a row of numbered boxes. Every box has an address, and every value here takes 8 bytes of it.
Names against addresses, why a copy is a second box, and what a pointer actually holds.
Names are for you, addresses are for the machine
When the program says a, the machine does not look up a name. The name existed while the code was being translated, and what came out the other side was an address. By the time anything runs, the box is only a number and its contents are only a number.
Each value here takes 8 bytes, and each new one goes in the next box along. Drag the slider and watch the addresses climb by 8 each time. That regularity is not a detail; the next lesson is built entirely on it.
A copy is a second box
Run copy to the end. Two boxes now hold 99 and 42, and nothing connects them. Writing to one cannot possibly affect the other, because they are different boxes and always were. The value was copied at the moment of assignment and the two have had separate lives ever since.
This is what “passing by value” means, and it is why a function that takes a number and changes it changes nothing anybody else can see.
A pointer is a box holding an address
Now run pointer. The new box holds the first box's address, written in the same notation as the addresses down the side, so you can match them by eye.
The next line writes through it. To follow a pointer, the machine reads the number in the box, treats that number as an address, and goes there. That is all dereference means. The value 99 lands in a, and the pointer itself is unchanged, because nothing was written to the pointer.
At the end a reads back as 99, having never been assigned to directly. After the copy it is still 42. Same program, same values, one line different.
Notice what is not drawn
There is no arrow. Every textbook draws one, curving from the pointer to the thing it points at, and that arrow is probably the single biggest reason pointers feel like magic: it makes the connection look like a thing that exists in the machine.
It does not. There is a box with a number in it, and the number happens to match another box's address. Nothing marks that box as special, nothing checks it, and if the number is wrong the machine will follow it anyway. Press null to watch that happen: the box holds zero, zero is not the address of anything, and the program stops.
What the addresses in a real program look like
The addresses here are consecutive and tidy, which is true of values declared together and not true in general. A running program has its memory in several regions, and which one a value lands in decides how long it lives far more than anything in the syntax does.
Values belonging to a call live together and vanish when the call returns. Values requested explicitly while the program runs live in a much larger region and stay until they are given back. The first is automatic and cheap; the second is manual and is where the interesting mistakes are.
The three ways a pointer goes wrong
- It names nothing. The
nullvariant above. Following it stops the program immediately, which sounds bad and is by far the kindest of the three, because it fails at the exact line responsible. - It names something that has gone. The box it refers to belonged to a call that has returned, or to memory that was given back. The address is still a perfectly good number and the machine will follow it happily into whatever is there now.
- It was never set. The box holds whatever was left in it, which is a number, which is an address, which the machine will follow. Nothing about this looks different from a correct pointer.
The second and third are the same problem in the end: a number that is no longer meaningful is indistinguishable from one that is. This is the entire reason modern languages went to such lengths, whether by taking the decision away from you, by tracking who owns what, or by refusing to compile code that cannot be shown to be safe.
Why anyone puts up with it
Handing over an address instead of a copy means handing over one small number instead of the whole thing. For a value made of a few bytes that is a wash. For a large one it is the difference between passing a reference and copying a megabyte.
It is also the only way for two parts of a program to agree about one piece of data rather than each holding their own version. Every structure in the rest of this course is built on that: a list holding the address of its next piece, a tree holding the addresses of its children, and everything after them holding the addresses of whatever comes next. One box that can name another is the primitive they all reduce to.
Languages without pointers have them anyway
In many languages you never write * or &, and objects are handled by reference automatically. Nothing about the machine changed: a variable holding an object still holds an address. What changed is that the language stopped making you say so and took responsibility for the three failures above.
Which is why the same question keeps arriving in a new costume. Two variables holding the same object, one of them changed, both appearing to change, is the pointer variant of this lesson with different syntax.
Three questions
Pick an answer before you open one. Being wrong here is the useful part, and it is the whole reason to answer rather than read.
A box in memory holds the number 2,147,417,152. What makes that number a pointer rather than an ordinary quantity?
Starting with a = 42, one program copies a and sets the copy to 99. Another takes a's address and writes 99 through it. What is a afterwards in each case?
Declaring 3 values one after another puts each in the next box along, 8 bytes apart. If the first is at address 1000, where is the third?
Problems
Work them before opening the answers. Reading a solution feels like learning and is not.
Work out the third address
Three values are declared in order.
The first is at address 2000, and each value
takes 8 bytes.
Where does the third one start?Show the answer →Hide the answer
2016.
2000 + 2 × 8. The third value has two in front
of it, not three.Counting the gaps rather than the boxes is the whole trick, and it is the reason indexes start at zero in almost every language.
Say what changes
let a = 5
let b = a
b = 9
What is a?Show the answer →Hide the answer
5.b was given a copy of the number in a's box, and b's box is a different box. After that assignment there is no relationship between them at all.
Say what changes, again
let a = 5
let p = &a
*p = 9
What is a, and what is p?Show the answer →Hide the answer
a is 9.
p is unchanged: it still holds a's address.Writing through a pointer never writes to the pointer. It is worth being deliberate about which of the two you mean, because the difference between *p = 9 and p = 9 is the difference between changing a value and pointing somewhere else entirely.
Why the swap does not stick
A function takes two numbers and swaps them.
Afterwards, the caller's variables are unchanged.
What would have to be passed instead?Show the answer →Hide the answer
Their addresses.The function was working on copies the whole time, and swapping two copies is a real swap of two boxes that nobody else can see. Given the addresses it can write to the caller's boxes, which is what makes it stick.
The number that is still a number
A function returns the address of one of its own
local values. The caller follows it and reads
something plausible.
What happened, and why is it worse than crashing?Show the answer →Hide the answer
The value belonged to the call, and the call
has returned. The address is still a valid
number, so the machine follows it into memory
that is now being used for something else.A crash names the line that caused it. This gives a wrong answer that looks like a right one, at a line with nothing wrong with it, often only under load. It is the single strongest argument for the languages that took the decision away from you.
What gets asked, and what a good answer sounds like
Say these out loud rather than reading them. The gap between knowing something and being able to say it is the thing interviews measure.
+What is a pointer?
A variable whose value is the address of another variable. Nothing about the number makes it an address; what makes it one is that the code treats it as somewhere to go rather than as a quantity.
+What is the difference between passing by value and passing a pointer?
Passing by value hands over a copy, so the two sides have separate boxes and neither can affect the other. Passing an address hands over the location, so both sides are working on the same box. That is the only difference, and every other consequence follows from it.
+What happens when you follow a pointer that names nothing?
The program stops at that line. It is the best of the bad cases, because it fails exactly where the mistake is. A pointer holding a stale address is worse: it is a valid number, so the machine follows it into memory being used for something else and the wrong answer surfaces somewhere unrelated.
+My language does not have pointers. Does any of this matter?
Yes, because the machine has not changed. A variable holding an object holds its address; the language simply stopped making you write it down and took on the job of never letting the address go stale. Two variables naming one object, one of them changed and both appearing to change, is exactly the situation above.
+Why are addresses useful at all?
Handing over one small number instead of a whole value, and letting two parts of a program agree about a single piece of data rather than each holding a version of it. Every structure built later is boxes that hold the addresses of other boxes.
Know someone stuck on this? Send it to them.
A classmate, a study group, someone learning this on their own at midnight. The link opens the lesson set up exactly as you have it, the same function and the same number, and runs from the first step so they watch the whole thing build rather than landing in the middle of it.
Next in this course
Arrays: why the index is free
Same size, one after another. That arrangement is what lets a position be calculated instead of searched for.
Read it next →