Memory and pointers
We have reached the most powerful and most delicate part of C. In this lesson we learn where variables live in memory, how to get their address, and how to control them directly through pointers. We see every idea in a live memory diagram, with addressed cells and pointer labels.
What you will learn in this lesson
6.1 What is memory?
Computer memory (memory or RAM) means a sequence of cells, numbered one after another, where data is stored. Each cell has its own ordinal number, and this number is called an address, just like the house numbers along a street.
When you create a variable in a program, for example int yosh = 19;, C gives one (or several) of these cells the name yosh and writes 19 there. In the past weeks we worked only with the value (19). Now we also see where that value lives, that is, its address.
Addresses are usually written in hexadecimal (hex) form, for example 0x100. The exact number does not matter to us; what matters is that every variable lives in a definite place in memory.
6.2 The address operator (&)
To get a variable's address, we put the & (ampersand) sign in front of it. For example &yosh means: which address the yosh variable lives at. Below are three variables in memory. Click a cell to see its address:
scanf("%d", &yosh) had exactly this same &. The reason: scanf needs to be given not the value but where to write, that is, the address.6.3 What is a pointer?
A pointer means a variable that stores the address of another variable. That is, inside a pointer there is not a value but an address. To create one, we put an asterisk in front of the type: int *p means p is a pointer that points to an int.
Below, choose which variable the pointer points to. The green label p is the pointer, and the cell it sits on is the variable being pointed to:
6.4 Dereferencing (*p)
Now the most interesting part. To read or change the value pointed to through the pointer, we use the asterisk again, but now it means dereferencing (following the pointer). *p means: the value at the place p points to.
If we write *p = 25;, this means "write 25 into the cell p points to". Since p points to yosh, yosh itself becomes 25. Try it:
p is an address, while *p is the value at that address. The two are different things. This is why pointers are powerful: you can give a pointer to a function, and it can change a variable that lives elsewhere.6.5 Arrays and pointers
Now the answer to the mystery from week 4. In C, an array's name is actually a pointer: it stands for the address of the array's first element. That is, arr and &arr[0] are the same thing. So you can write int *p = arr;, and p now points to the start of the array.
Below is the array in memory. The green p label points to the start of the array. Click a cell to see each element's address:
6.6 Pointer arithmetic
Since an array's name is a pointer, we can also do arithmetic on a pointer. p + 1 means "move to the next element". But here is the subtle point: it moves not by 1 byte but by one element (4 bytes for an int). So *(p + 1) is the same as arr[1].
Below, walk the pointer along the array. Watch the green p label move, and the address and the *p value change:
Make a prediction
What does this program print to the terminal?
6.7 Strings and pointers
Now the truth about strings is also revealed. In week 4 we saw that a string is a char array. And an array is a pointer. So a string is actually a char pointer (char *) that points to the first letter.
Here ism points to the first letter A. *ism gives us A (dereferencing), while ism + 1 moves to the next letter l. This is exactly why you can also walk through a string with a pointer, up to the \0 at the end.
printf("%s", ism), printf is given not the whole string but only the starting address. printf then reads the letters from that address onward until it reaches \0. It is all built on pointers.6.8 Dynamic memory (malloc)
Until now the size of variables was known in advance. But sometimes a program knows how much memory it needs only while it is running, for example how many numbers the user will enter. In that case we use dynamic memory: the malloc function allocates memory of the required size and returns its address (a pointer).
Below, choose a size, request memory with malloc, and when you are done, free it with free:
free. Otherwise it stays occupied. This problem is called a memory leak.6.9 The stack and the heap
A program's memory is divided into two main parts. Your ordinary variables live in one place, while memory obtained with malloc lives in another. These are called the stack and the heap:
- Ordinary, local variables live here
- Automatic: when a function ends, it cleans itself up
- Fast, but limited in size
- For example:
int yosh = 19;
- Dynamic memory (malloc) is taken from here
- Managed by hand: you free it yourself with free
- Large and flexible, but it needs care
- For example:
malloc(...)
The key difference: the stack cleans itself up, while the heap is your responsibility. That is exactly why every malloc must be matched by one free.
6.10 Going deeper advanced
Pointers are powerful, but they require care. Here are the most common concepts and mistakes.
NULL pointer
NULL is a special value that means "points to nowhere". When a pointer is not yet ready to be used, it is given NULL. Dereferencing a pointer that points to NULL (*p) crashes the program, so you must check first.
Dangling pointers and memory leaks
There are two widespread mistakes. A dangling pointer: a pointer still pointing to memory that has been freed, and using it is dangerous. A memory leak: calling malloc but forgetting to free, so the memory stays needlessly occupied.
p = NULL;. Then it does not stay in a dangling state, and it becomes easy to check later.Glossary of terms
6.11 Knowledge quiz
16 questions. To complete the week, answer at least 11 of them correctly.
Congratulations! Week 6 is complete
Now you understand the most delicate topic in C, memory and pointers: addresses, pointers, dereferencing, pointer arithmetic and dynamic memory. This is where many people get stuck, but you made it through.
Next week: Data structures (struct, linked list, stack and queue).
Go to the next module