Memory Allocation

You could've noticed that all arrays and strings we used before in the course had fixed sizes. That is, when we created an array, we knew how large it was:

char s[100]; /* an array of 100 chars */

That is not always the case: more often than not, you don't know how large the data your program needs to handle will be. Imagine a text editor; it needs to be able to handle both small files and large files. Of course, you could just allocate some fixed amount of memory (as an array of chars) and refuse to store more, but that's not perfect.

Here comes dynamic memory allocation: we can ask the operating system to allocate some memory for us, and use it in our program just as you would use an array. When you're done with that memory, let the system know.

To do that, we'll need two functions from the C standard library: malloc and free. To use them, include stdlib.h. But before we talk about them, we need to learn about void pointers.

Void pointers

When we talked about pointers and strings, you saw that a pointer always knows the type of the value it points to, for example, int * points to an int, and char * points to a char. A function that allocates a memory of the given size and returns a pointer to the allocated memory does not know how you are going to use that memory. You might put some integers there, or some characters, or, actually, anything you want.

For that reason, C has a special void pointer type, void *. The value of this type is just a pointer, but we don't know which specific type it points to, and we cannot dereference it. But we can assign it to any other pointer variable, such as int * or char *.

Note: there is a difference between C and C++ here. In C++, such an assignment will require an explicit type cast. I normally don't talk about C++ in this course, but this is one thing which will break if you compile your code with a C++ compiler and not C compiler.

Allocating memory

To allocate some memory for your program, use malloc(size), where size is the number of bytes you need. It returns a void pointer to the new memory, or a special "null pointer" NULL if something went wrong; NULL corresponds to address 0 which is considered invalid. In the modern systems it is very unlikely that you will ever have malloc return NULL and it probably does not make much sense to check it in the small programs.

How many bytes to allocate? Normally you allocate memory to use it as you would use an array. But note that malloc needs number of bytes, not number of your array elements: if you want an int array of size 10, you probably need 40 bytes if int is 4 bytes on your system.

How do you know exactly how many bytes you need? You can use sizeof operator. The common pattern for memory allocation is:

/* N is an integer, number of elements */
int *p = malloc(N * sizeof(*p));
if (!p) {
	perror("malloc");
}

Let's look at this snippet closer.

First of all, malloc returns void *, but we immediately assign it to a variable of type int *, so we could use p as an integer array later.

Then, to calculate number of bytes needed, we look at the size of the type p points to (sizeof(*p)–not sizeof(p)!) and multiply it by the number of elements we need.

Can we write sizeof(int) instead? Yes, it will work, but writing sizeof(*p) is preferred because if you ever decide to change the type, say, from int to char, you will change it in just one place (int *p = to char *p =), and sizeof(*p) will still be correct, while sizeof(int) will need to be changed, and people often forget it.

Finally, if malloc returned NULL, p will be NULL, which is 0, so if (!p) checks for that condition and prints an error message using the standard function perror from stdio.h.

After this snippet, p can be used just like any array of N integers.

Freeing memory

When you don't need your allocated memory anymore, use free(p) to return this memory to the system.

Technically, in very short programs, you don't need to do it: any memory not freed will be automatically returned when your program exits. But whenever you write a program that runs for longer, freeing memory becomes important, because if you only allocate, but never free, your program uses more and more memory, and eventually runs out of it. It's a good habit to keep track of all the memory you allocated, and free it when you don't need it anymore.

Many modern languages don't require developers to do that, offering garbage collection instead: automatic disposal of unused memory. In C, there is no such luxury.

So, the summary of this page is:

On the next page I'll give some examples of using malloc and free.

© Alexander Fenster (contact)