Skip to main content

Section 22.4 Dynamic Memory

As we have seen before, the term static generally refers to things that are done or known at compile time, while dynamic refers to things that happen at runtime. Although all the stack based memory is not actually allocated until the program runs, it is still considered static because the amount of memory needed for each function is known at compile time. In contrast, allocations made to the heap are considered dynamic because the amount of memory needed can change at runtime.
To do a dynamic allocation, we use the new operator in C++ followed by the type we want to allocate:
new int;
new Person;
We can use () to initialize the data. For example:
new int(5);         // Allocates an int initialized to 5
new Person("Bob");  // Allocates a Person by calling a constructor that takes a string
These allocations are placed on the heap and are not associated with any stack frame. Given this program:
#include <iostream>
#include <string>

using namespace std;

void foo() {
    // Stack based memory
    int y = 2;
    
    // Heap based memory
    new int(5);
    new string("Hello, world");
    // --end of foo()--
}

int main() {
    int x = 1;
    foo();
    // --after foo() returns--
}
At the point where the comment indicating the end of foo is, the memory layout would look something like this:
Memory layout after dynamic allocation in foo()
Notice that the 5 and Hello, world are stored on the heap, while x and y are stored on the stack in frames associated with main and foo, respectively. Also notice that items on the heap do not have identifiers (names). As written, there is no way to access those pieces of memory! We will solve that problem in a minute, but for now, let’s look at what happens after foo returns.
When foo returns, the stack frame for foo is destroyed, and y is no longer accessible. However, the memory allocated on the heap for the int and string still exists. This is a key feature of dynamic memory: it persists beyond the scope in which it was allocated. Back in main, where the comment indicates foo() has ended, the memory would now look like:
Memory layout after foo returns
So how do we access the dynamically allocated memory if they do not have identifiers? The answer is to keep track of the pointers to the allocated memory. When we use new, it returns a pointer to the allocated memory. We can store this pointer in a variable, and then use it later to access the memory.
This improved version of the code creates pointers to store the addresses of the dynamically allocated memory. When new is called, the value it returns is stored into one of the pointers. We can then use those pointers to access the heap memory:
Listing 22.4.1.
This time, the memory layout at the end of foo() would look like:
Memory layout with p1 and p2 that point at allocated heap memory
Key things to note:
  • The values 5 and Hello, world are stored on the heap.
  • p1 and p2 are local variables in foos stack frame. They store the memory addresses that new returned.
  • In the image, memory addresses 0x40 and 0x80 are used as examples. When you run the program, you will get different (much larger) values.

Insight 22.4.1.

Stack based variables have identifiers (names) that allow us to access them easily. Heap based values do not have identifiers, so we must use pointers to access them. Every heap memory access will start from a stack based variable that points to the heap allocated memory.

Checkpoint 22.4.1.

Based on what you know about the stack and heap, what will happen when foo returns?
Memory layout with p1 and p2 that point at allocated heap memory
  • The memory allocated for variables y, p1, and p2 will be automatically cleaned up.
  • Correct! The memory allocated on the stack persists until it is explicitly freed.
  • The memory allocated for the values 5 and Hello, world will not be automatically freed, leading to a memory leak.
  • Correct, the memory allocated on the heap persists until it is explicitly freed.
  • The memory allocated for variables y, p1, and p2 will not be automatically freed, leading to a memory leak.
  • Those are all a part of the stack frame for foo that will be removed automatically.
  • The memory allocated for the values 5 and Hello, world will be automatically cleaned up.
  • The memory allocated on the heap must be explicitly freed.

Checkpoint 22.4.2.

Allocate a double on the heap with the value 3.14 and assign its address to a pointer named PIish .

Checkpoint 22.4.3.

The pointer pData points at an integer. Which line of code correctly prints the value of that integer?
  • std::cout << *pData;
  • Correct! We need to dereference the pointer to access the integer that is on the heap.
  • std::cout << &pData;
  • That would print the address of the pointer variable itself (the address of where pData is stored on the stack).
  • std::cout << pData;
  • That prints what is stored in pData, which is the address of the integer that is on the heap.
You have attempted of activities on this page.