CMPT 225–Call Stack & Heap Memory
The call stack
- Suppose a function
A
calls a functionB
, which callsC
. - During execution control passes from (the code for)
A
, toB
, then toC
. - The execution of
C
ends, control must return toB
and then toA
.
- At each function call, the system records where control should return to be pushing an activation record on the call stack
- The call stack also records all local variables, including the arguments to the function call.
Call Stack Illustration
Code is in call_stack.cpp
Call stack for this process:
Name | Data |
---|---|
… | … |
main | a=1, b=0 |
f(2) | c=2, d=3, e=8 |
g(4) | i=4 |
… | … |
g | <code for g> |
f | <code for f> |
main | <code for main> |
Stack Code
Picture of code, which can be found in stack_pointers.cpp
Picture of code output, which can be found in stack_pointers_output.txt
Dynamic Memory or Heap
- Vairables declared in functions are stored on the call stack
- These variables:
- are of fixed size
- are destoryed when the function they are defined in terminates
- We often want a function
f
to create data that can be used after it returns.- In particular, dynamic data structures require this!
- This data is stored in “the heap”, a region of memory that is allocated dynamically as needed!
In C++:
- Basic (or primitive) types can be stored on the call stack or on the heap.
- Objects (e.g. instances of classes) can be stored on the call stack or on the heap.
- Variables declared in functions are on the stack
- Allocation on the heap is denoted by “new”.
Ex: Basic Types on Call Stack & Heap
Code snippet below:
f(){
int n; //n is on stack
n=6;
int * np;//np is on stack
np = new int;// new int is stored in heap
*np = 7; // np points to the location.
}
Ex: Basic Types on Call Stack & Heap
See code above.
Call stack:
Name | Value | Note |
---|---|---|
n | 6 | |
np | pointer | np is a pointer to the location on the heap. |
Heap:
Value |
---|
... | 7 |
... |
Note: When f
ends, np
is gone (the stack is popped), but the space it pointed to is not.
Class Instances on Heap & Stack (combines notes on two slides)
Code snippet:
List * f(){
List L;
List * Lp;
Lp = new List();
return Lp; // returns pointer
}
main(){
...
List * lst = f(); // lst becomes a pointer to the list object
...
}
Call Stack:
Name | Values | Notes |
---|---|---|
lst | pointer to heap | in main |
L | <List Object> | entire List object; in function f (destroyed when f ends) |
Lp | pointer to <List Object> | in function f (this pointer is also destroyed when f exits); annotation: this instanciates the list class |
Heap:
Value | Notes |
… | … |
<List Object> | entire list object |
… | … |
Accessing Instance Members in C++ (combination of notes in two slides)
Suppose a class Store with:
- a data member
x
. (an int) - a function
put(v)
that storesv
inx
. - a function
get()
that returns the value ofx
.
Consider this code fragment:
f(){
...
Store s1;
s1.put(5);
y=s1.get();//y=5
...
Store *s2;
s2=new Store();// (annotation: XX)
s2.put(5);// (annotation: X)
y=s2.get()// (annotation: X)
...
*s2.put(5);// (annotation: X)
y=*s2.get();// (annotation: X)
...
(*s2).put(5);// (annotation: check mark)
y=(*s2).get();// (annotation: check mark)
...
s2->put(5); // equiv. to (*s).put(5)// (annotation: check mark)
y=s2->get(); // equiv. to y=(*s).get()// (annotation: check mark)
}
Call Stack:
Name | Value | Notes |
---|---|---|
s2 | pointer to <Store Object> | |
s1 | <Store Object [x=5]> |
Heap:
Value | Notes |
---|---|
<Store Object [x=5]> |
Using the Textbook List Class
See textbook_list.cpp and List.h.
Call stack during the run of the program:
Name | Value | Notes |
---|---|---|
N | 5 | within main |
lst | <List Object [size=5, head=0, tail=4]> | within main |
The heap is empty.
See “doubly linked list” example in lecture 05.
The List Class
Two images of the doubly linked list slides from lecture 05 with a big red X written overtop each image.
The List Class - Constructor
Code snippet:
private:
int theSize;
Node *head;
Node *tail;
void init()
{
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
};
After constructor:
Name | Next | Value | Prev |
---|---|---|---|
head | pointer | nullptr | nullptr |
tail | nullptr | nullptr | pointer |
Code snippet form List.h:
Node(const Object& d = Object{}, Node* p = nullptr, Node* n = nullptr)
: data{d}, prev{p}, next{n} {}
The List Class - The iterators
- Data member (Node* current)
- a pointer to a Node. (the list iterators are implemented with pointers.)
- Constructors:
- turn a pointer into an iterator:
iterator(Node\* p): const_iterator{p}{}
const_iterator(Node\* p): current{p}
- turn a pointer into an iterator:
- function:
iterator end(){ return iterator(tail}
- turns the tail pointer into the iter to “end”. (???can’t read completely)
- it corresponds to “just past the end” of the list
The List Class - the push_back function
// add an element to the tail end of the list
// end() is the end iterator
// x is the element to add
void push_back(const Object& x){insert(end(), x);}
iterator insert(iterator itr, const Object& x){
Node *p = itr.current; // turns the iterator into a pointer
++theSize; //increments size variable
return iterator(p->prev=p->prev->next=new Node(x, p->prev, p)) // turns the pointer into an iterator
// p->prev=p->prev->next=new Node(x, p->prev, p)) does two things:
// 1. Makes a new node `N`
// 2. stores a pointer to `N` in p->prev and it p->prev->next
}
The List Class - Inserting the first element (combination of notes from two slides)
Code snippet:
// itr is the end
iterator insert(iterator itr, const Object& x){
Node *p = itr.current;// itr.current is the tail
++theSize;
return iterator(p->prev=p->prev->next=new Node(x, p->prev, p)); // turns pointer into iterator
// x is new list element
// p->prev is initial value of prev
// p is initial value of next
}
List pointed to by pointer p (original)
Name | Next | Value | Prev |
---|---|---|---|
head | pointer | nullptr | nullptr |
tail | nullptr | nullptr | pointer |
The “new Node(…)” object:
Next | Value | Prev |
---|---|---|
pointer | nullptr | pointer |
Transcriber’s addition (implied in diagram): What does p->prev=p->prev->next=New Node(...)
do to this list?
Transciber’s note: It inserts the new Node(...)
into the list like so:
Name | Next | Value | Prev |
---|---|---|---|
head | pointer | nullptr | nullptr |
new Node(...) |
pointer | nullptr | pointer |
tail | nullptr | nullptr | pointer |
Transciber’s note: This cancels the previous connection between head
and tail
directly.
Using the Textbook List Class
Code snippet:
#include "dsexceptions.h"
#include "List.h"
int main(){
const int N = 5;
List<int> = lst;
for(int i=N-1; i<0; --i){
lst.push_front(i);
}
return 0;
}
When lst contains the two values [1, 2]:
Call Stack:
Name | Value | Notes |
---|---|---|
N | 5 | |
lst | <List Object [theSize=2, head=pointer, tail=pointer> |
Heap (transcriber’s note: the link takes you to the value, but the pointer is to the group of values [next, value, prev), A.K.A. a Node):
Value | Notes |
Node.next | Node index 0 |
Node Value: 1 | Node index 0 |
Node.prev | Node index 0 |
… | … |
Node.next (nullptr) | Ending Node |
Node Value: ? | Ending Node |
Node.prev | Ending Node |
… | … |
Node.next | Node index 1 |
Node Value: 2 | Node index 1 |
Node.prev | Node index 1 |
… | … |
Node.next | Beginning Node |
Node Value: ? | Beginning Node |
Node.prev (nullptr) | Beginning Node |
Linked List Ends
Transcriber’s note: tell me if you like this way of transcribing simplified linked lists where the individual values don’t matter, but only a linear relationship between the nodes:
For [4, 5, 6]:
versus
For [5]:
versus
For []:
versus (transcriber’s note: the lack of connection is intentional)
End
After the “End” slide, some previous slides are repeated for no apparent reason.