In the first module, we talked about local variables and scope.
I said that a place in memory needs to be allocated
to store these local variables, and when the scope is finished,
that place in memory is deallocated.
However, this general chunk of memory from RAM,
which is assigned to our running program,
is actually divided in several logical units.
One of these units is named stack,
and this is the place in memory where local
variables get stored upon declaration.
The reason why it's called a stack is because this piece
of memory is structured like a stack, which is a last in,
first out data structure.
Every time you declare a new function scope,
a so‑called stack frame is pushed onto the stack.
Basically,
a stack frame is just a wrapper for all of the
variables declared in that specific scope.
Every declared variable will allocate a piece of memory
next to the previous memory allocation.
So, for example, once this first variable is declared,
the next one will allocate a piece of memory right next to it.
If you can imagine that this memory is oriented vertically,
you can see that it looks kind of like we stacked
variables on top of each other, hence the name for this place in memory.
The details of the exact implementation are not important to
us because the stack is managed by the CPU.
However,
a simplified way of thinking about the memory allocation
process in stack is to imagine that the CPU keeps track of a
pointer called a stack pointer.
Every time we allocate space for a new variable,
this stack pointer is moved at the end of the stack
to the first free byte of memory.
This is how CPU knows where the top of the stack is at each moment.
Since the main function calls another function,
this new stack frame gets pushed onto the stack,
and all of the declared local variables and
parameters are stored next to each other.
Once this function is done executing,
this local variable from the function scope needs to be released from memory.
And since the CPU is using a simple pointer for allocation,
the only thing it needs to do is to move this pointer back,
which will pop this variable off the stack,
therefore deallocating this block of memory.
This is how scope works.
Of course,
CPU needs to do a little bit more than this to remember where each
variable is and where each stack frame begins.
But as I said,
this is a good enough explanation for what we need to know to move on.
You may wonder why I didn't put local variables next to
each other in memory when we were visualizing memory
allocations in the previous module.
This is because that layer of abstraction was not required
for you to understand how pointers work.
And the only reason I'm showing you how stack works now
is so that you can compare it to heap,
and heap is another important region of memory,
which we can use to allocate space for our objects.
A lot of C++ programmers also refer to this region as free store.
Both names basically mean the same thing,
but I will continue to refer to it as heap.
This is another reason why we need pointers because they allow
us to access memory allocated on the heap.
To allocate memory on the heap, we can use the keyword new.
After that, we need to specify how much memory we want to allocate,
and we can do that by providing the data type,
which can be a primitive one or a user‑defined class.
If we put integer here,
this will allocate four bytes of memory on the heap and return an address
to the first byte in a form of a pointer to an integer.
We need to store this memory address somewhere.
I will declare a pointer and assign it to this returned address.
This is why we need pointers for allocating on the heap
because when we allocate with this new keyword,
all we get back is a memory address.
That also means that this allocated space on the heap is
not tied to the surrounding scope.
The new keyword will allocate enough memory on the heap to store an integer.
However, this pointer itself will be stored on the stack.
Only the memory address it points to is taken from the heap.
Don't be confused by this new keyword.
Everything you declare as a local variable is still
stored in the stack part of memory.
If you take a look at memory addresses on this slide,
you may assume that stack and heap are really close to each other in memory.
I just wanted to let you know that this is random.
We are not interested in how these two memory
regions are implemented by the system.
All I really want you to take out of this lesson is that
you can see them as distinct entities.
So let's say that this declaration is placed inside of some special scope,
which is nested inside of the main function.
What do you think will happen when this inner scope ends?
Just like we learned in the previous module,
this pointer will get popped off the stack.
So after this closing curly brace, the pointer does not exist anymore.
However, this allocated space in memory on the heap is still there,
and this is the root of all memory management problems,
which inspired the evolution of C++ language in the
domain of dynamic memory allocation.
A memory allocated on the heap always needs to be
explicitly deallocated by the programmer.
The new operator has a complimentary delete operator,
which you can use to deallocate memory on the heap.
All you need to provide to the delete operator is the pointer to the
place in memory which needs to be deallocated.
Of course,
this deletion needs to happen inside of this scope because the
pointer will cease to exist after this scope ends.
As you can see,
this means that the memory allocated on the heap does not
automatically get deleted when we leave the surrounding scope.
This is both a blessing and a curse.
It is a blessing because sometimes you want to have some more durable data so
that you don't have to depend on the surrounding scope,
but you need to be aware that you always need to have at least one
pointer pointing to the memory allocated on the heap.
In other words,
you cannot lose track of the memory address from the heap because if you do,
you cannot use delete to deallocate that place in memory.
And if you don't use delete,
that memory on the heap will stay allocated until the whole program is finished.
This might not seem like a big problem in this example.
Once this scope ends, the main function is done anyway.
However, what if this was declared in some other function,
which is called at the beginning of a large program?
Losing track of memory from the heap is also known as memory leak,
and we will see an example of this later in the module.