C++ Memory Management - The Stack

An introduction to memory management within C++, starting with stack-allocated memory.
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

7c.jpg
Ryan McCombe
Ryan McCombe
Posted

In the beginner course, we introduced the concept of the call stack, which is generated by the function calls in our program.

We saw how we could create local variables within our functions. We may have predicted, therefore, that the stack has some memory available to store things required by our functions.

This is indeed the case - the stack has a small amount of memory available to it. When we create variables in our functions, we are given the appropriate amount of memory from the stack to store those variables.

When the function ends, the stack frame is removed, local variables are deleted, and the memory is freed up for other uses.

We can see this in action using a class with a constructor and destructor:

#include <iostream>
using namespace std;

class Character {
public:
Character() {
  cout << "Creating Character" << endl;
}
~Character() {
  cout << "Destroying Character" << endl;
}
};

void SelectCharacter() {
  Character Frodo;
}

int main() {
  cout << "Program Starting" << endl;
  SelectCharacter();
  cout << "Program Ending" << endl;
}

The output is as follows:

Program Starting
Creating Character
Destroying Character
Program Ending

If the concept of constructors and destructors are unfamiliar, I’d recommend reviewing this lesson:

Limitations of Stack Allocation

This automated form of memory management is very useful, but it does have some limitations.

  1. The amount of memory available on the stack is limited, so we need alternative techniques once we start creating larger objects
  2. The fact that objects are automatically deleted and their memory freed restricts our options

For an example of the second limitation, consider a scenario where we want our SelectCharacter function to return a pointer to the character:

#include <iostream>
using namespace std;

class Character {
public:
  ~Character() {
    cout << "Destroying Character" << endl;
  }
  string Name { "Frodo" };
};

Character* SelectCharacter() {
  Character Frodo;
  return &Frodo;
}

int main() {
  Character* SelectedCharacter { SelectCharacter() };
  cout << "Getting Character Name:" << endl;
  cout << SelectedCharacter->Name << endl;
}

On my machine, this program outputs the following:

Destroying Character
Getting Character Name:
1�I�^H�H�PTI�#@H�`#@H�@�* �D�f.�@�@`H=�@`t�H�t

The fact that line 3 was nonsense is perhaps predictable given the proceeding output. The Character has already been destroyed because it was allocated within the SelectCharacter function’s stack frame.

So, the Frodo pointer within our main function is pointing at memory that is no longer allocated to our program.

This is going to cause issues but, hopefully, the compiler will have warned us of this. Clang generated the following warning:

warning: reference to stack memory associated
with local variable 'Frodo' returned

Returning stack-allocated data by value

Updating the previous example to return Frodo by value rather than by reference would have worked as expected:

Character SelectCharacter() {
  Character Frodo;
  return Frodo;
}

int main() {
  Character SelectedCharacter { SelectCharacter() };
  cout << "Getting Character Name:" << endl;
  cout << SelectedCharacter.Name << endl;
}
Getting Character Name:
Frodo
Destroying Character

When we return something from a function, that data is moved to the stack frame that called our function. In this example, it’s the main function that receiving Frodo

When we return Frodo by value, we’re moving the Frodo object. Now, the Frodo object is not destroyed when SelectCharacter ends. It is instead moved to main, and only destroyed once main ends.

Previously, when we returned a pointer, we’re moving just the pointer to the main function. The Frodo object is still within the SelectCharacter stack frame. Therefore, it gets destroyed with the stack frame, and the pointer that was moved to main is no longer useful.

We cover move semantics in more detail later in this course.

The Free Store

To get around these limitations, there is another place in memory where we can store data. This is called the free store.

There, we have much more control over the lifecycle of our objects. Most notably, our functions can place objects there, and they remain in place until we explicitly delete them.

We’ll see how we can use the free store in the next lesson.

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

7a.jpg
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access!

This course includes:

  • 106 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Dynamic Memory - The Free Store and the Heap

Learn about dynamic memory in C++, and how to allocate objects to it using new and delete
a61.jpg
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved