Team 2550
Technical Documentation
 All Files Variables Pages
Dynamic Memory

At some point, arrays become too big to store on the stack. This is called stack overflow.

#include <iostream>
using namespace std;
void overflow(double x);
int main() {
overflow(0);
}
//For demonstration purposes, this function uses recusion.
void overflow(double x) {
int a[1000][500];
cout << x << '\n';
overflow(x + 1);
}

Output

0
1
2
3
Segmentation fault (core dumped)

The sole purpose of the function above is to allocate a lot of memory. As programs get larger and more complex, it becomes increasingly difficult to stay within the bounds of the memory that the operating system gives your program when it is launched. In order to use more memory, you have to manually allocate it as needed at runtime. This is where dynamic memory comes in. C++ has some special built-in functions that request more memory from the operating system and return pointers to that extra space. The cost of dynamic memory is that it is much slower: by using it, you sacrifice speed for flexibility.

Note
A recursive function is a function that calls itself in order to reduce the complexity of a problem. They can take up a lot of stack space as every function call is placed on the stack. The video below explains recursion fairly well.

The new Operator

In order to dynamically allocate a value, you have to use the new operator – it requests the operating system for a block of memory and then returns a pointer to that location.

int* x = new int;
*x = 73; //Note the * before the x. It is very easy to forget to add that.
cout << &x << ' ' << *x << '\n';

Output

0x7fff82376378 73
Warning
Every new operation must have a corresponding delete. Read about that in the next section.

The delete Operator

The delete operator de-allocates memory so that the operating system can use it again.

#include <iostream>
using namespace std;
int main() {
int* x = new int;
*x = 38;
cout << *x << '\n';
delete x;
cout << *x << '\n';
}

Output

38
0

There is one problem in the above code. Look at the output, the second value is 0. However, this could easily be something else. delete de-allocates the memory so that it can be overwritten by the operating system. It is always best to assign the pointer to some other location of NULL.

#include <iostream>
using namespace std;
int main() {
int* x = new int;
*x = 38;
cout << *x << '\n';
delete x;
cout << *x << '\n';
x = NULL;
cout << *x << '\n';
}

Output

38
0
Segmentation fault (core dumped)
Note
The segmentation fault is good in this case – it allows for easier debugging.

The use of the delete operator is necessary when working with dynamic memory. Failure to do so will create a memory leak. A memory leak occurs when a pointer pointing to dynamic memory is changed or inaccessible. For instance, a function that does not use the delete operator on an internal pointer. You can no longer access that value, and the system cannot take it back. This can cause problems if your program uses a large amount of memory.

Note
Sometimes you will see calls to the malloc() and free() functions in older code. These functions are the equivalent to new and delete in C. C++ still supports them for historical reasons. Do not use them in your code. They completely ignore constructors and destructors.

Array Allocation

Arrays can be allocated easily. Just use the new and delete operators.

int* x = new int[10000]; //Allocates
delete [] x; //De-allocates

You can access the elements of the array using the standard array subscript notation.

x[103] = 32;

Large arrays are not the only benefit of dynamic arrays. The size of a stack-allocated array is set at compile-time, however, the size of a dynamic array can be determined at runtime. The code below should result in a compiler error.

#include <iostream>
using namespace std;
int main() {
int s;
cin >> s;
int x[s];
for (int i = 0; i < s; ++i)
x[i] = i;
for (int i = 0; i < s; ++i)
cout << x[i] << '\n';
}
Note
Some compilers extend the C++ standard to allow a variable to define the length of a stack-allocated array. Even though it is techincally possible, the use of features such as this is dicouraged because it reduces the portability of the code.

The following code shows one example where you would want to use a dynamic array. It prompts the user to put in an array size, requests a value for each element, and then prints the final array.

#include <iostream>
using namespace std;
int main() {
int* x;
unsigned int x_size;
cout << "Enter the array size: ";
cin >> x_size;
x = new int[x_size];
for (unsigned int i = 0; i < x_size; ++i)
{
cout << "Input x[" << i << "]: ";
cin >> x[i];
}
cout << '\n';
for (unsigned int i = 0; i < x_size; ++i)
cout << "x[" << i << "] = " << x[i] << '\n';
delete [] x;
x = NULL;
}

Input/Output

Enter the array size: 8
Input x[0]: 431
Input x[1]: 541
Input x[2]: 63
Input x[3]: 1
Input x[4]: 34
Input x[5]: 63524
Input x[6]: 1543155
Input x[7]: 6513

x[0] = 431
x[1] = 541
x[2] = 63
x[3] = 1
x[4] = 34
x[5] = 63524
x[6] = 1543155
x[7] = 6513

Allocating a multidimensional array is more complex. You have to loop through each additional level and use pointers to pointers.

#include <iostream>
using namespace std;
int main() {
int** x = new int*[100]; //Allocate an array of pointers to pointers
for (int i = 0; i < 100; ++i)
x[i] = new int[1000]; //Allocate an array of pointers
x[10][300] = 30;
cout << x[10][300] << '\n';
for (int i = 0; i < 100; ++i)
delete [] x[i]; //De-allocate the array of pointers
delete [] x; //De-allocate the array of pointers to pointers
}

It is important to realize that allocating a multi-dimensional array to the heap is significantly different from the stack. Instead of the allocation in the multidimensional arrays section, heap-allocated memory can be wherever the system finds free space. This results in a noticeable speed reduction.

Instead of a table like this...

x[3][2] in Memory
Address Name
1000 x[0][0]
1001
1002
1003
1004 x[0][1]
1005
1006
1007
1008 x[1][0]
1009
1010

1011

1012 x[1][1]
1013

1014

1015

1016 x[2][0]
1017

1018

1019
1020 x[2][1]
1021
1022
1023

A dynamically allocated array with the same dimensions would look more like this...

Dynamic Objects

Objects can be dynamically allocated as well. Here, we will cover two situations: classes containing pointers and dynamically-allocated classes.

Pointers as Members

Classes containing pointers typically allocate memory in the constructor and de-allocate using the destructor. Let's create a dummy class to demonstrate this.

FILE: Dummy.hh

#ifndef DUMMY_HH
#define DUMMY_HH
#include <iostream>
class Dummy {
private:
static const int s;
int* a;
public:
Dummy(); //The constructor
~Dummy(); //The destructor
bool setElement(int i, int v);
int getElement(int i);
};
#endif

FILE: Dummy.cc

#include "Dummy.hh"
using namespace std;
const int Dummy::s = 1000000;
Dummy::Dummy() {
cout << "Dummy constructor run.\n";
a = new int[s];
}
Dummy::~Dummy() {
cout << "Dummy destructor run.\n";
delete [] a;
}
bool Dummy::setElement(int i, int v) {
if (i >= s)
return false;
a[i] = v;
return true;
}
int Dummy::getElement(int i) {
return a[i];
}

FILE: test.cc

#include <iostream>
#include "Dummy.hh"
using namespace std;
int main() {
Dummy test;
test.setElement(1000, 43125);
test.setElement(13, 15);
}

Output

Dummy constructor run.
Dummy destructor run.

Dynamic Objects

Every time you create a class, you actually create two data types: the class datatype and the class pointer datatype. In other words, you create DummyClass and DummyClass*. When using a pointer to a class, new calls the constructor and delete calls the destructor. The files below demonstrate how class pointers work.

FILE: Dummy.hh

#ifndef DUMMY_HH
#define DUMMY_HH
#include <iostream>
class Dummy {
private:
int a[100];
public:
Dummy(); //The constructor
~Dummy(); //The destructor
void setElement(int i, int v);
int getElement(int i);
};
#endif

FILE: Dummy.cc

#include "Dummy.hh"
using namespace std;
Dummy::Dummy() {
for (int i = 0; i < 100; ++i)
{
a[i] = 0;
}
cout << "Dummy constructor run.\n";
}
Dummy::~Dummy() {
cout << "Dummy destructor run.\n";
}
void Dummy::setElement(int i, int v) {
a[i] = v;
}
int Dummy::getElement(int i) {
return a[i];
}

FILE: test.cc

#include <iostream>
#include "Dummy.hh"
using namespace std;
int main() {
Dummy* test = new Dummy;
test->setElement(10, 43125);
test->setElement(13, 15);
cout << test->getElement(10) << ' ' << test->getElement(13) << '\n';
delete test;
test = NULL;
}

Output

Dummy constructor run.
43125 15
Dummy destructor run.

Although the above class is basically useless, it does demonstrate how class pointers work. There are certain situations where a particular class stores a lot of information but you need a very large number of them. This is the kind of situation where it could be useful to store an array of class pointers or a dynamically allocated array of class pointers.

Note
Again, you have to use the -> operator as opposed to the dot operator.

Further Research

Dynamic memory has many practical uses. I would encourage you to research data structures, as they use dynamic memory to organize large amounts of data efficiently.