Dynamic Memory Allocation
Allocating memory
There are two ways that memory gets allocated for data storage:
- Compile Time (or static) Allocation
- Memory for named variables is allocated by the compiler
- Exact size and type of storage must be known at compile time
- For standard array declarations, this is why the size has to be constant
- Dynamic Memory Allocation
- Memory allocated "on the fly" during run time
- dynamically allocated space usually placed in a program segment known as the heap or the free store
- Exact amount of space or number of items does not have to be known by the compiler in advance.
- For dynamic memory allocation, pointers are crucial
Dynamic Memory Allocation
- We can dynamically allocate storage space while the program is running, but we cannot create new variable names "on the fly"
- For this reason, dynamic allocation requires two steps:
- Creating the dynamic space.
- Storing its address in a pointer (so that the space can be accesed)
- To dynamically allocate memory in C++, we use the new operator.
- De-allocation:
- Deallocation is the "clean-up" of space being used for variables or other data storage
- Compile time variables are automatically deallocated based on their known extent (this is the same as scope for "automatic" variables)
- It is the programmer's job to deallocate dynamically created space
- To de-allocate dynamic memory, we use the delete operator
Allocating space with new
- To allocate space dynamically, use the unary operator new, followed by the type being allocated.
new int; // dynamically allocates an int new double; // dynamically allocates a double
- If creating an array dynamically, use the same form, but put brackets with a size after the type:
new int[40]; // dynamically allocates an array of 40 ints new double[size]; // dynamically allocates an array of size doubles // note that the size can be a variable
- These statements above are not very useful by themselves, because the allocated spaces have no names! BUT, the new operator returns the starting address of the allocated space, and this address can be stored in a pointer:
int * p; // declare a pointer p p = new int; // dynamically allocate an int and load address into p double * d; // declare a pointer d d = new double; // dynamically allocate a double and load address into d // we can also do these in single line statements int x = 40; int * list = new int[x]; float * numbers = new float[x+10];
Notice that this is one more way of initializing a pointer to a valid target (and the most important one).
Accessing dynamically created space
- So once the space has been dynamically allocated, how do we use it?
- For single items, we go through the pointer. Dereference the pointer to reach the dynamically created target:
int * p = new int; // dynamic integer, pointed to by p *p = 10; // assigns 10 to the dynamic integer cout << *p; // prints 10
- For dynamically created arrays, you can use either pointer-offset notation, or treat the pointer as the array name and use the standard bracket notation:
double * numList = new double[size]; // dynamic array for (int i = 0; i < size; i++) numList[i] = 0; // initialize array elements to 0 numList[5] = 20; // bracket notation *(numList + 7) = 15; // pointer-offset notation // means same as numList[7]
Deallocation of dynamic memory
- To deallocate memory that was created with new, we use the unary operator delete. The one operand should be a pointer that stores the address of the space to be deallocated:
int * ptr = new int; // dynamically created int // ... delete ptr; // deletes the space that ptr points to
Note that the pointer ptr still exists in this example. That's a named variable subject to scope and extent determined at compile time. It can be reused:
ptr = new int[10]; // point p to a brand new array
- To deallocate a dynamic array, use this form:
delete [] name_of_pointer;
Example:
int * list = new int[40]; // dynamic array delete [] list; // deallocates the array list = 0; // reset list to null pointer
After deallocating space, it's always a good idea to reset the pointer to null unless you are pointing it at another valid target right away.
- To consider: So what happens if you fail to deallocate dynamic memory when you are finished with it? (i.e. why is deallocation important?)
Application Example: Dynamically resizing an array
If you have an existing array, and you want to make it bigger (add array cells to it), you cannot simply append new cells to the old ones. Remember that arrays are stored in consecutive memory, and you never know whether or not the memory immediately after the array is already allocated for something else. For that reason, the process takes a few more steps. Here is an example using an integer array. Let's say this is the original array:
int * list = new int[size];
I want to resize this so that the array called list has space for 5 more numbers (presumably because the old one is full).
There are four main steps.
- Create an entirely new array of the appropriate type and of the new size. (You'll need another pointer for this).
int * temp = new int[size + 5];
- Copy the data from the old array into the new array (keeping them in the same positions). This is easy with a for-loop.
for (int i = 0; i < size; i++) temp[i] = list[i];
- Delete the old array -- you don't need it anymore! (Do as your Mom says, and take out the garbage!)
delete [] list; // this deletes the array pointed to by "list"
- Change the pointer. You still want the array to be called "list" (its original name), so change the list pointer to the new address.
list = temp;
That's it! The list array is now 5 larger than the previous one, and it has the same data in it that the original one had. But, now it has room for 5 more items.