Search code examples
c++memorymemory-leaksarduinoavr-gcc

Can this code cause a memory leak (Arduino)


I have a arduino project and I created this struct:

struct Project {
  boolean         status;
  String          name;
  struct Project* nextProject;
};

In my application I parse some data and create Project objects. To have them in a list there is a pointer to the nextProject in each Project object expect the last. This is the code where I add new projects:

void RssParser::addProject(boolean tempProjectStatus, String tempData) {
  if (!startProject) {
    startProject = true;

    firstProject.status      = tempProjectStatus;
    firstProject.name        = tempData;
    firstProject.nextProject = NULL;

    ptrToLastProject = &firstProject;
  } else {

    ptrToLastProject->nextProject = new Project();

    ptrToLastProject->nextProject->status      = tempProjectStatus;
    ptrToLastProject->nextProject->name        = tempData;
    ptrToLastProject->nextProject->nextProject = NULL;

    ptrToLastProject = ptrToLastProject->nextProject;
  }
}

firstProject is an private instance variable and defined in the header file like this:

Project firstProject;

So if there actually no project was added, I use firstProject, to add a new one, if firstProject is set I use the nextProject pointer.

Also I have a reset() method that deletes the pointer to the projects:

void RssParser::reset() { 
  delete ptrToLastProject;
  delete firstProject.nextProject;

  startProject = false;
}

After each parsing run I call reset() the problem is that the memory used is not released. If I comment out the addProject method there are no issues with my memory. Someone can tell me what could cause the memory leak?


Solution

  • First of all, you do not need the variable startProject - simply initialize the firstProject pointer with NULL and then write your condition like this:

    if (firstProject)
    {
        // There is a project, so append the new one.
    }
    else
    {
        // There is no project, so we need to create a new list.
    }
    

    The value FALSE is defined as 0, just as NULL. If firstProject is NULL the expression would look like if (FALSE) and continue execution inside the else-block.

    So now your reset-Method needs to free the memory allocated for all projects, not only the last one and the second one, as your code does.

    delete ptrToLastProject; // Free last project
    delete firstProject.nextProject; // Free the project following to the first one.
    

    The problems in here are:

    • what if ptrToLastProject == firstProject.nextProject? The second delete-statement would free already released memory.
    • firstProject gets never released
    • the projects between the second and the last ones will never be released.

    The best way to free a singly linked list is something like this:

    Project* pProject = firstProject;
    Project* pProjectToDelete;
    
    while (pProject)  // As long as the pointer points to something (see the first comment)
    {
        pProjectToDelete = pProject;
        pProject = firstProject->nextProject;
        delete pProjectToDelte;
    }
    

    In this implementation you are "walking" through the list, releasing the predecessing element, as long as there is an element following. If the next element is NULL, the last element has been released and the loop breaks.

    Last but not least you need to reset the pointer to the first element (also called "anchor" in the terms of data structures):

    firstProject = NULL;
    

    this ensures that addProject does not try to append an project to NULL.