Originally published by Robert Beisert at fortcollinsprogram.robert-beisert.com

Linux + C – Malloc and Free

In the last post, we employed dynamic memory allocation. Today, we’re looking at that concept in further detail.

Allocating Memory

The function malloc() – “memory allocate” pulls a section of the heap – memory that exists outside of the usual program stack which is used to store data members – and returns it to you. The size of that section is determined by you – if you tell malloc() to give you 8 bytes of memory, it will give you 8 bytes of memory. That would look like this:

malloc( (size_t) 8)

Note: We have to cast the integer 8 to the data type size_t, because malloc() expects a size_t measurement. This is the type of measurement generated by measuring functions already installed in the C language.

It’s both hard and confusing to try to remember the size of absolutely every data type we employ. This is where the function sizeof() applies; it returns an integer detailing the number of bytes which a data type occupies. For example, if we want to allocate a block the size of an integer, we can write the following:

mallocsizeof  (int)  )  )

Finally, we have to realize that malloc() can return any type of pointer to this data. It doesn’t care whether it allocates a block for characters, integers, lists, or pictures. When we use malloc(), we have to cast the pointer – that is, tell the program what kind of pointer it will deal with.

Allocating an integer would therefore look something like this:

int * panda = (int *) malloc (sizeof (int) )

Yin and Yang: Creation and Destruction

Whenever we create a thing, we also have to destroy it. Otherwise, it continues to take resources that we could otherwise use.

The opposite of malloc() is free(). free() takes a pointer to a region of data, and makes it available again.

Writing a free() operation is much simpler than writing a malloc() operation. When we want to free our integer pointer “panda”, we simply write:

free(panda)

…and it is done.

Note: When the item we want to free has pointers inside of itself (like with a list), we often want to free those pointers as well. This can be tremendously complicated, if you aren’t careful, but failing to free memory causes a lot of problems.

bad_malloc.c

WARNING: The following code contains a fatal memory leak. Running it will slow down your computer, and may result in a crash (usually non-fatal). Execute this only in a virtual machine or sandbox.

The following program allocates data like crazy, but does not free anything. It makes your computer very sad.


#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
//This will run until the program crashes or your computer does
while(malloc(sizeof(int)))
{

}

//Let's face it: you won't see this happen.
return 0;
}

Constructors and Destructors

As we said before, for every malloc() there must be a free(). Because a constructor is a function which creates an object using malloc(), it makes sense to have a destructor function which destroys an object using free().

There are a two simple rules for destructors:

  • Always free() the pointers inside of the object before you free the object
  • Always free from bottom to top
  • Don’t bother trying to free something that isn’t a pointer – it doesn’t work

These are good practices that generally keep you out of trouble.

Note: One reason to use destructor functions is to prevent yourself from trying to free a NULL pointer. In Linux, this usually results in a segment fault that crashes your program.

linked_list_two.c

This is the same as our previous linked list, only now we have several new destructor functions. Make sure to notice the differences between the two files.


#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;time.h&gt;

//We'll create this structure with a unique ID value called measurement_number
//We often want to create a unique ID for our list objects
struct temperature_list
{
int measurement_number;
int fahr_temp;
struct temperature_list *next;
};

//Look at how easy it is to find the next object in the list
/**
Retrieve the next item in the list
@param current    The current item in the list
@return        A pointer to the next item in the list
**/
struct temperature_list * get_next(struct temperature_list *current)
{
if(current ==NULL)
return NULL;

return current-&gt;next;
}

//It's not hard to use a while() loop to get to the last object.
//However, on a large list, this can take a little while.
/**
Retrieve the last item in our list
@param first    A pointer to the first element in the list
@return        A pointer to the last object in the list
**/
struct temperature_list * get_last(struct temperature_list *first)
{
if(first ==NULL)
return NULL;

struct temperature_list *navi = first;
while(navi-&gt;next != NULL)
navi = navi-&gt;next;
return navi;
}

//Meh. Couldn't find a good reason to use this function this time. Still, this is what it could look like.
/**
Find an item in the list by its unique value, measurement_number
@param first    A pointer to the start of the list
@param measurement    The ID value we're looking for
@return The next object on success,
**/
struct temperature_list * find(struct temperature_list * first, int measurement)
{
if(first ==NULL || measurement &lt; 0)
return NULL;

struct temperature_list *navi = first;

while(navi-&gt;measurement_number != measurement &amp;&amp; navi-&gt;next != NULL)
navi = navi-&gt;next;
if(navi-&gt;measurement_number != measurement)
return NULL;
else return navi;
}

/**
Add a new measurement to the list
@param first    A pointer to the first item (or an empty list)
@param temp    The temperature value to store
@return        0 on success
**/
int new_measurement(struct temperature_list *first, int temp)
{
struct temperature_list * new_temp;
struct temperature_list *navi = first;

if(navi == NULL)
return 1;

navi = get_last(navi);
//malloc() can be tricky. We'll talk about it in detail later.
//More importantly, we'll talk about destroying things we've created with malloc() later.
new_temp = (struct temperature_list *) malloc(sizeof( struct temperature_list));
navi-&gt;next = new_temp;
new_temp-&gt;measurement_number = navi-&gt;measurement_number + 1;
new_temp-&gt;fahr_temp = temp;
new_temp-&gt;next = NULL;

return 0;
}

/**
We always want to create a destructor for every constructor - though this one is simple
@param item    The item to be freed
@return     0 on success
**/
int free_temperature_list(struct temperature_list *item)
{
free(item);
return 0;
}

/**
Get the previous item - useful for removing an item at the end of the list
@param    first    The first item in the list
@return        The previous item or NULL (if only one item)
**/
struct temperature_list *get_previous(struct temperature_list *first, int measurement)
{
struct temperature_list *navi = first;

//If there's only one item in the list, do nothing
if(navi-&gt;next == NULL)
{
return NULL;
}

//Otherwise, there are at least 2 items, so this function has meaning
while(navi-&gt;next != NULL)
{
//Escape the while loop if we find what we're looking for
if(navi-&gt;next-&gt;measurement_number == measurement)
break;
else
navi = navi-&gt;next;
}

return navi;

}

/**
Remove a measurement from the list
@param first    The first item in the list
@param measurement    The measurement number to destroy
@return 0 on success (error in this case will usually kill the program...)
**/
int destroy_measurement(struct temperature_list *first, int measurement)
{
struct temperature_list *prev = get_previous(first, measurement);
struct temperature_list *tokill = get_next(prev);

prev-&gt;next = tokill-&gt;next;
free_temperature_list(tokill);

return 0;
}

/**
Free every item still in the list
@param first    The first item in the list
@return 1 if list is empty, 0 on success
**/
int free_all_items(struct temperature_list *first)
{
//Empty list? Nothing to do!
if(first == NULL)
return 1;
else
{
struct temperature_list *next_item;

while(first-&gt;next != NULL)
{
next_item = first-&gt;next;
free_temperature_list(first);
first = next_item;
}

free(first);

return 0;
}
}

int main(int argc, char *argv[])
{
int ops;
int i;
if(argc &lt; 2)
{
ops = 10;
printf(&quot;Default 10 elementsn&quot;);
printf(&quot;Next time, usent./linked-list number_of_elementsn&quot;);
}
else ops = atoi(argv[1]);

//We'll put a bunch of random values into the temperature list
srand(time(0));

//The first item in the list requires special work - we'll put it here, instead of creating a new function
struct temperature_list *list;
list = (struct temperature_list *) malloc(sizeof(struct temperature_list));
list->next = NULL;
list->measurement_number = 1;
list->fahr_temp = rand() %150;

//When I was working on this, I needed to make sure it worked fine.
//This is an example of debugging/error-handling code
if(list == NULL)
{
printf(&quot;ERROR - NO LIST!n&quot;);
return 1;
}

//Time to populate the list. See how easy it is?
//Note that, because we already have an item #1, we'll get a total of ops+1 measurements.
for(i = 0; i < ops; i++)
{
printf(";Creating item #%d\n", i);
new_measurement(list, rand() % 150);
}

//We don't want to accidentally lost the start of the list. That means we have to create a new pointer.
struct temperature_list *navigator = list;
if(navigator == NULL)
printf(&quot;ERROR!n&quot;);

//This works basically the same way as get_last(), only we print the values out as we go!
//Note that the pointers are not necessarily consecutive, like they were in our array earlier.
//...but they may be, in this case. After all, we're creating them one after another.
while(navigator != NULL)
{
printf(&quot;%ptMeasurement #%d:    %dn&quot;, navigator, navigator-&gt;measurement_number, navigator-&gt;fahr_temp);
navigator = get_next(navigator);
}

//Let's kill measurement #10. It'll be interesting to see what happens if we don't have a measurement 10...
destroy_measurement(list, 10);

navigator = list;
//Look at the difference
while(navigator != NULL)
{
printf(&quot;%ptMeasurement #%d:    %dn&quot;, navigator, navigator-&gt;measurement_number, navigator-&gt;fahr_temp);
navigator = get_next(navigator);
}

//Clean up after ourselves
free_all_items(list);

return 0;
}

 

malloc() vs. calloc()

In the same library as malloc() we find a function called calloc(), which most programmers use instead of malloc.

Why do they prefer calloc()? It turns out that, while malloc() gives you a chunk of memory to use, it doesn’t change the original contents of that memory. If the previous contents were a picture of a dog, that picture is still there after we allocate it to our program.

calloc(), on the other hand, fills the memory with zeros before it gives it back to us. This means that we have a clean slate to work with.

Generally speaking, you should use calloc().