As discussed in Chapter 10, when declaring an array, the size declarator must be either a literal or a constant, and may not be a variable. The following program from Chapter 10 attempts, unsuccessfully, to use a variable numTests in declaring the size of an array:
#include <iostream <iostream>>
The result is a compiler error. The compiler will flag the declaration of the array (int testScore[numTests]) and complain that a constant expression was expected. The reason a constant (or literal) expression is required is that in this program we are allocating memory for the array at compile time. The compiler needs to know exactly how much memory to allocate. However, if a variable is the size declarator, the compiler does not know how much memory to allocate because a variable’s value may change. Indeed, in the preceding example, the value of the variable used as the size declarator is not even known until runtime.
Having said this though, it is often desirable to have the user determine at runtime the size of the array so it is neither too small nor too large, but just right. To accomplish this, you need to declare the array using dynamic memory allocation. The following program modifies the previous one to use dynamic memory allocation:
#include <iostream <iostream>>
Some sample input and output follows:
Enter the number of test scores: 3
Dynamic memory allocation works, even though using a variable as a size declarator does not, because with dynamic memory allocation, memory is not being allocated at compile time. Instead, memory is being allocated at runtime, and from a different place (the “heap”) rather than where it is allocated at compile time (the “stack”).
NOTE: The terms heap and stack also have meaning in data structures. Here, however, these terms are used to identify different areas of memory: the stack for memory allocated at compile time; the heap for memory allocated at runtime.
While you could dynamically allocate a single variable, normally dynamic memory allocation is used with arrays (as in this example), or with objects such as structures or classes, which are discussed in Chapter 14.
You need to use a pointer to dynamically allocate memory. The pointer must be of the same data type as the array that is to be allocated dynamically. An assignment statement is used, as in the following statement from the program:
int * iPtr = new int[numTests];
The pointer is on the left side of the assignment operator. Immediately to the right of the assignment statement is the new operator, whose purpose is to dynamically allocate memory. The array that is to be allocated dynamically immediately follows the new operator, described by data type and a size declarator in a subscript , but with no array name. The size declarator may be a variable instead of a literal or constant.
Since the array has no name, it and its elements are referred to through the pointer that created it, such as iPtr[i] in the for loops used to assign values to and output the values of the array elements. Therefore, the scope of the dynamically created array is the same as the scope of the pointer used to declare it.
The significance of dynamic memory allocation is not scope, but lifetime. Like a global variable or a static local variable, the lifetime of a dynamically created variable is as long as that of the program’s execution. However, if before the end of the program the pointer that points to a dynamically created variable goes out of scope, you no longer have any way of accessing the dynamically created memory. Therefore, the dynamically created variable still takes up memory, but is inaccessible. This is called a memory leak.
Having programs that dynamically allocate memory but never release it is akin to a library where patrons check out books but never return them. Sooner or later the library will run out of books, and the computer will run out of memory.
A memory leak is not a particular concern in the preceding program since the pointer that points to the dynamically allocated memory does not go out of scope until immediately before the program ends. However, if you dynamically allocate memory inside a function using a local pointer (as in a program in the next section), then when the function terminates, the pointer will be destroyed but the memory will remain, orphaned since there is no longer a way of accessing it for the remainder of the program.
You release dynamically allocated memory with the delete operator. Just as the new operator is used to create dynamically allocated memory, the delete operator is used to return dynamically allocated memory to the operating system. The syntax is the delete operator followed by the pointer that points to the dynamically created memory. Additionally, if the dynamically created memory is an array as opposed to a single variable, then empty subscripts  are placed between the delete operator and the pointer, as in the following statement from the program:
delete  iPtr;
While the delete operator operates on a pointer, the delete operator does not delete the pointer. Instead, the delete operator deletes the memory at the address pointed to by the pointer.
NOTE: You should only use the delete operator with a pointer that points to dynamically created memory. Using the delete operator with a pointer that points to memory created on the stack rather than from the heap can lead to unpredictable results.
Finally, since the pointer is the only way to which you can refer to the dynamically allocated variable, you should not change the value of the pointer to point to a different address unless you first assign a different pointer to the dynamically allocated memory. Otherwise, you no longer have a way of accessing the dynamically created memory. The result would be a memory leak.
blog comments powered by Disqus