Week 6 · Foundation

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

Understand that memory is a sequence of addresses
Get a variable's address with the & operator
Create a pointer and make it point to another variable
Read and change the pointed-to value with *p
See how arrays and pointers relate, and how pointer arithmetic works
Work with dynamic memory using malloc and free

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.

Pointers feel hard at first, and that is normal. Keep the main idea in mind: the value is the thing inside the cell, while the address is the cell's number. A pointer works precisely with addresses.

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:

Address viewer click a cell
main.c
1int yosh = 19;
2int narx = 500;
3int soni = 3;
Click one of the cells above: you will see its address (&).
Remember, last week 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.

pointer.c
1int yosh = 19;
2int *p = &yosh; // p points to yosh

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:

Pointer diagram choose the pointer target
p =
A pointer itself also lives in memory and has its own address. But its value is the address of another variable. That is exactly why we say a pointer "points": it refers to some place.

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:

Dereferencing field enter a value
*p =
Note carefully: 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.

massiv.c
1int arr[4] = {10, 20, 30, 40};
2int *p = arr; // p = &arr[0]

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:

Arrays and pointers click a cell
Click one of the array cells: you will see its index and 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:

Pointer arithmetic move p

Make a prediction

What does this program print to the terminal?

main.c
1int arr[] = {5, 10, 15};
2int *p = arr;
3printf("%d", *(p + 2));
10
15
5

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.

satr.c
1char *ism = "Ali";
2printf("%c\n", *ism); // A (the first letter)
3printf("%s\n", ism); // Ali (the whole string)

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.

So in 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).

dinamik.c
1int *p = malloc(3 * sizeof(int));
2p[0] = 10; // we use it just like an array
3free(p); // once done, we free it

Below, choose a size, request memory with malloc, and when you are done, free it with free:

malloc and free request memory
size:
The most important rule: every block of memory obtained with malloc must be freed with 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:

Stack
  • Ordinary, local variables live here
  • Automatic: when a function ends, it cleans itself up
  • Fast, but limited in size
  • For example: int yosh = 19;
Heap
  • 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.

Good practice: right after freeing, immediately set the pointer to p = NULL;. Then it does not stay in a dangling state, and it becomes easy to check later.

Glossary of terms

addressa variable's place in memory, obtained with &.
pointera variable that stores the address of another variable.
*pdereferencing: the value at the place the pointer points to.
mallocallocates dynamic memory from the heap at run time.
freefrees memory that was obtained with malloc.
NULLa special pointer value that points to nowhere.

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