Dynamic Memory

It is possible to allocate memory dynamically, that is, at execution time. It is also possible to deallocate the memory involved, giving it back to the operating system.

Suppose we have a pointer:

     int * P;

As declared, this is just a pointer, and it as yet has no value, so *P is meaningless. We could arrange for it to point to some existing int, as in

     int K;
     P = &K;

Instead, we could allocate a new int variable:

     P = new int;

This obtains from the operating system enough memory (at some location) to store an int. Now *P has a meaning. We could actually initialize it at the same time:

     P = new int(5);

and now *P has the value 5.

Later, we can change the value of P, as in

     P = &K;

but if we do this, we should first get rid of the existing *P:

     delete P;

Otherwise, there is a danger of creating a memory leak: the chunk of memory containing the value 5 (the old *P) might be orphaned, that is, left without anything pointing to it. In that case, the memory involved simply sits there, idle and useless, until the program ends. Memory leaks can accumulate and eventually cripple the system.

After we use delete, the pointer P should be regarded as having no value. In practice, it might still have the same value, but the memory in question may already be in use for some other purpose.

A similar problem occurs if we have two pointers with the same value:

     int * A;
     int * B;
     A = new int(17);
     B = A;

Now suppose we say

     delete A;
     A = 0;

The memory containing the value 17 has been given back to the operating system, and we have reinitialized A. What value is in B? B contains the same value as before, so B contains a meaningless value. There is a danger now of trying to use *B. This is a dangling pointer.

Syntax for allocation:

     Type * Pointer;
     Pointer = new Type;

where Type might be one of the elementary types such as int or char, or Type might be a user-defined type such as a class or struct.

Syntax for deallocation:

     delete Pointer;

where we do not have to provide Type; the compiler will look this up in the declaration of Pointer.


What if we want an array?

We can allocate an array dynamically as well. Suppose we want an array of N floats where N is an int value. We start with

     float * A;
and then
     A = new float[N];

This allocates enough memory (in one contiguous block) for N float values, and it puts the address of the first one in A. This makes it possible to deal with A as if we had started with an array called A; we can refer to A[0], A[1], etc.

If this seems odd, bear in mind that the name of an array is exactly a constant pointer to the first element of the array. That is,

     float H[20];

and

     float * (const H) = new float[20];

are equivalent.

Later, we can deallocate the array:

     delete [] A;

Notice that we do not need to provide the array size here. The compiler will generate code to look up the size used when the array was created. We do, however, need the extra []. If we simply wrote

     delete A;

the result would be that A[0] is released back to the operating system, but the rest of the array would be orphaned. The delete operator will delete one item. (After all, A was declared as a pointer to just one float.)

Thus we have four operators here: new, delete, new[] and delete[].


How was this done in C?

In the C language, the same abilities existed, implemented using functions called malloc and free. If we have

     float * A;

we can create a float to be *A as follows:

     A = (float *) malloc(sizeof(float));

The malloc function takes one argument, an integer number of bytes, and it returns a void * pointer to the newly allocated chunk of memory. We then cast the void * pointer as whatever we need.

The sizeof operator gives us the number of bytes needed to store something.

To allocate an array, we simply indicate a larger size:

     A = (float *) malloc(N * sizeof(float));

To deallocate the memory involved, we use the free function:

     free(A);

where free will be able to deallocate the correct number of bytes. (It presumably consults a list of who has used malloc recently.)

The functions malloc and free (and some related functions) are in <cstdlib>. They can be used in C++, but we should not mix them with new and delete. Memory allocated with malloc is deallocated with free, and memory allocated with new (or new[]) is deallocated with delete (or delete[]).

In passing: the sizeof operator is unusual in that its argument is not ordinary data but instead is the identifier of a type of variable (or of a variable).