# Memory Management

This section introduces basic memory management functions which will prove useful in solving the extra assignments.

{% tabs %}
{% tab title="malloc" %}
**What does it do?**

* Dynamically allocates memory of a certain size on the heap. The allocated memory is uninitialized.
* Linux man page: `man malloc`.

**Signature**&#x20;

```c
void *malloc(size_t size);
```

* **The first argument (size)** represents the size of the memory segment to allocate, in bytes.

**Analogy**

Imagine you are a librarian; you have just received a packet of a known number of books and you have available a number of shelves in your library. You need to find an empty space on a shelf where you can store all your books. The books are new and more resistant to dust particles, so you do not need a clean shelf. Once you find such an empty space, place the books there. If no space was found, then you donate them.

Similarly, you give `malloc` a certain size that you need for your program (the number of books) and `malloc` will try to find in your memory a segment (empty space on a shelf) big enough to fit the requested size. If it finds one, malloc will return a pointer to the start address of the allocated memory (in the library example this would be the shelf number) which you can use to access that memory block. If it does not find one, malloc will return `NULL` (the packet was not placed on a shelf).
{% endtab %}

{% tab title="realloc" %}
**What does it do?**

* Resizes a block in the memory that was previously allocated with `malloc`, `calloc`, or `realloc`.
* Linux man page: `man realloc`.

**Signature**

```c
void *realloc(void *ptr, size_t new_size);
```

* **The first argument (ptr)** represents a pointer to a memory segment, which was allocated with `malloc`, `realloc,` or `calloc`.
* **The second argument (new\_size)** represents the new size of the memory segment to which **ptr** points to.

**Analogy**

Imagine you want to celebrate your performance in the ''Computer Organization 2024-2025'' course, so you book a table at a restaurant for $$n$$ people. More of your friends decide to join, so you need to call the restaurant to change your reservation from $$n$$ people to $$m$$ people $$(m \ge n)$$, in order for all your friends to sit together at the same table.

Similarly, you previously allocated a memory block of a certain size with `malloc` and you have a pointer ptr returned by the `malloc` call. At some point in time you decide that you need a bigger memory block, so you decide to use `realloc` to resize it. `realloc` will take as parameters your pointer ptr and the new size and will try to find in the memory of your computer a new memory block big enough. Once it finds such a block, `realloc` will move the content of the previous memory block to the new memory block and it frees the previous memory block. At the end, it will return a new pointer ptr that will indicate the start address of the new memory block. If `realloc` does not find such a block, it will return `NULL`.

**Note:** `realloc` may return the same pointer if the current memory block could be extended _in-place_ (similarly to putting two tables together). Additionally, `realloc` can also shrink memory blocks if the parameter `new_size` is smaller than the originally allocated size.
{% endtab %}

{% tab title="calloc" %}
**What does it do?**

* Allocates initialized memory blocks for a number of objects of a certain size. The memory blocks will be initialized with 0.
* Linux man page: `man calloc`.

**Signature**

```c
void *calloc(size_t num, size_t size);
```

* **The first argument (num)** represents the array size (number of objects).
* **The second argument (size)** represents the size of each object of the array.

**Analogy**

Imagine that you buy a number of limited edition books of the same size.  By knowing the number of books, you need to find a space on a shelf that is big enough so that all the books fit. When you find such a space, you will clean the space of dust and you will place the books there.&#x20;

Similarly, `calloc` will receive a number of n objects of a certain size m and will try to find n contiguous memory blocks of size m to allocate for those objects. When it finds them, `calloc` will initialize all blocks with 0 and then it will return a pointer that points to the address of the first object. If `calloc` does not find enough available space for all objects, then it will return NULL.

**Similarity with malloc**

Now you may be wondering how `malloc` and `calloc` do the same thing, except the initialization part. Well, think about a block of size k as a number of n blocks of size m such that k = n x m. Consider k to be a block of 64 bits, n to be 8, and m a block of 8 bits. Then when you make a malloc call (depending on the purpose of the memory allocation) you can  write it as:

```cpp
int64_t *ptr = (int64_t*)malloc(sizeof(int64_t));
```

or as:

```cpp
int8_t *ptr = (int8_t*)malloc(8 * sizeof(int8_t));
```

The later malloc call looks similar to a `calloc` call in which you provide as arguments n as the number of objects and m as the size of an object. Such a call could look like:

```cpp
int8_t *ptr = (int8_t*)calloc(8, sizeof(int8_t));
```

As a result, `malloc` and `calloc` have similar functionality. Very important to remember is the fact that `calloc` initializes the allocated memory blocks with 0, while `malloc` does not.
{% endtab %}

{% tab title="free" %}
**What does it do?**

* Deallocates a memory block that was previously allocated by calling `malloc`, `calloc` or `realloc`.
* Linux man page: `man free`.

**Signature**

```c
void free(void *ptr);
```

* **The first argument (ptr)** represents a pointer to a memory segment that was allocated with `malloc`,  `realloc,` and `calloc`.

**Analogy**

Imagine that you allocated some space on a shelf for a certain number of books and you decided to donate them. Once you remove the books from the shelf, the shelf is no longer assigned to those books, so you can store new books on it.

Similarly, when you decide that a previously allocated memory block is no longer needed, by calling `free`, you mark that memory block as "not in use" and, in the future, you will be able to make new memory allocations on that memory region (the new memory allocation does not have to be of the same size as the old one).&#x20;

{% hint style="danger" %}
You should always **free** the memory allocations not use anymore to avoid memory leaks.
{% endhint %}
{% endtab %}
{% endtabs %}

