User Tools

Site Tools


cplusplus:pointers_4

This is an old revision of the document!


Pointers 4

Dynamic memory, memory management, pointers as return types.

This content has not been vetted for C++11 compliance.

Dynamic memory allocation

Dynamic memory allocation allows you to reserve blocks of computer memory for use as storage but without associating a variable name with it. This allocation happens at runtime, hence the term dynamic memory allocation. It is used most often used with pointers.

The ''new'' operator

The new operator is used to allocate dynamic memory. new <data-type> reserves a block of memory large enough to hold the specified <data-type> and returns the base address of that block:

double *foo;      // pointer to a double
foo = new double; // allocate an unnamed block of memory
                  // large enough to hold a double
                  // and set foo to point to it.

foo now points to a double that is not a variable.

simple-allocation.cpp
/** Dynamically allocate and use two doubles. */
#include <iostream>
using namespace std;
 
int main()
{
    double *myPtr, *yourPtr;
 
    myPtr = new (double);
    yourPtr = new (double);
 
    cout << "Enter a number: ";
    cin >> *myPtr;
    cout << "Enter a number: ";
    cin >> *yourPtr;
 
    cout << "The average of the two numbers is " << (*myPtr + *yourPtr)/2.0 << "." << endl;
 
    return 0;
}

Dynamic allocation of arrays

Dynamic memory allocation can be used to allocate an array:

dynamic-array.cpp
/** Dynamically allocate and use an array. */
#include <iostream>
using namespace std;
 
int main()
{
    const int SIZE = 10;
    double *arrayPtr = new double[SIZE];  // create block to hold array
 
    /* You can use subscript notation [] to access an array: */
    for(int i = 0; i < SIZE; i++)
    {
        arrayPtr[i] = i * i;
    }
 
    /* or pointer arithmetic: */
    for(int i = 0; i < SIZE; i++)
    {
        cout << *(arrayPtr + i) << endl;
    }
 
    return 0;
}

If there is not enough memory available to allocate the desired block, BadThings™ will happen.

Memory Management

Memory management of regular variables

Regular local variables are destroyed when the lifetime of the scope where they are declared ends. In the example below, localVar is destroyed at the end of the function call along with the storage associated with it:

local-var-memory.cpp
/** Example showing local variable lifetime. */
#include <iostream>
using namespace std;
 
void ninetynine();
 
int main()
{
    ninetynine();
 
    // ... do some other stuff ... //
 
    return 0;
}
 
void ninetynine()
{
    int localVar = 99;
    cout << localVar << endl;
}

Thus, the management of memory associated with regular local variables is automatic.

Memory management of dynamically allocated storage

Dynamically allocated memory is not automatically managed in C++. In the example below, the variable localPtr is destroyed at the end of the function call, but the dynamically allocated storage for the int pointed to by localPtr is not.

memory-loss.cpp
/** Example showing lifetime of dynamically allocated storage and a 
  * a small memory leak.
  */
#include <iostream>
using namespace std;
 
void ninetynine();
 
int main()
{
    ninetynine();
 
    // ... do some other stuff ... //
 
    return 0;
}
 
void ninetynine()
{
    int *localPtr = new int;
 
    *localPtr = 99;
    cout << *localPtr << endl;
}

Because the pointer is the only way to access the allocated block, there is no way to do anything useful with the block after the function terminates and main enters the … do some other stuff … part of the program. The dynamically allocated memory block is still taking up space, and there's no way to make another pointer to point to that block.

Memory leaks

The above is an example of a memory leak. It's a tiny one: when the program is run, it uses 4 or 8 or whatever (i.e., sizeof(int)) more bytes of RAM after the function returns than it needs to. This may seem like nothing worth losing sleep over, but what if ninetynine() appears in a loop or is otherwise executed repeatedly?

memory-leak.cpp
/** Example of a sizable memory leak. */
#include <iostream>
using namespace std;
 
void ninetynine();
 
int main()
{
    for (int i=0; i<10000; i++)
    {
        ninetynine();
    }
 
    // ... do some other stuff ... //
 
    return 0;
}
 
void ninetynine()
{
    int *localPtr = new int;
 
    *localPtr = 99;
    cout << *localPtr << endl;
}

This adds up to a lot of wasted RAM. Memory leaks, no matter how small, are considered bad programming practice. Fortunately, they can be fixed by the proper use of deallocation.

Deallocation

Deallocation is the process of releasing back to the OS storage that was previously dynamically allocated.

Any memory that has been dynamically allocated must be deallocated (i.e., released back to the OS) somewhere in the program, otherwise a memory leak will result.

Since deallocation of dynamically allocated storage does not happen automatically in C++, you must explicitly (i.e., manually) deallocate the memory. The delete operator lets you explicitly deallocate memory that has been dynamically allocated.

The code below fixes the memory leak introduced above:

deallocation.cpp
/** Example showing deallocation. */
#include <iostream>
using namespace std;
 
void ninetynine();
 
int main()
{
    ninetynine();
 
    return 0;
}
 
void ninetynine()
{
    int *localPtr = new int;
 
    *localPtr = 99;
    cout << *localPtr << endl;
    delete localPtr;  // deallocates block pointed to by localPtr.
}

To deallocate dynamically allocated arrays, use square brackets: delete [] arrayPtr;

Gaddis-Pr9-14.cpp
// This program totals and averages the sales figures for any
// number of days. The figures are stored in a dynamically
// allocated array.
#include <iostream>
#include <iomanip>
using namespace std;
 
int main()
{
    double *sales = nullptr,	// To dynamically allocate an array
    total = 0.0,				// Accumulator
    average;					// To hold average sales
    int numDays,				// To hold the number of days of sales
        count;					// Counter variable
 
    // Get the number of days of sales.
    cout << "How many days of sales figures do you wish ";
    cout << "to process? ";
    cin >> numDays;
 
    // Dynamically allocate an array large enough to hold
    // that many days of sales amounts.
    sales = new double[numDays];
 
    // Get the sales figures for each day.
    cout << "Enter the sales figures below.\n";
    for (count = 0; count < numDays; count++)
    {
		cout << "Day " << (count + 1) << ": ";
		cin >> sales[count];
    }
 
    // Calculate the total sales
    for (count = 0; count < numDays; count++)
    {
		total += sales[count];
    }
 
    // Calculate the average sales per day
    average = total / numDays;
 
    // Display the results
    cout << fixed << showpoint << setprecision(2);
    cout << "\n\nTotal Sales: $" << total << endl;
    cout << "Average Sales: $" << average << endl;
 
    // Free dynamically allocated memory
    delete [] sales;
    sales = nullptr; // Make sales a nullptr.
 
    return 0;
} 

heap vs. stack

Up to now, we may have given the impression that local variables and dynamically allocated storage are both allocated memory from the same amorphous pool of RAM. This is not the case. The stack is a pool of memory whose allocation and deallocation is automatically managed. Local variables (and global ones too) are allocated from the stack. The heap is a pool of memory whose allocation and deallocation is explicitly (i.e., manually) managed. Dynamically allocated memory are allocated from the heap.

A more detailed discussion of the heap versus the stack, while important, is beyond the scope of the present discussion. But it is important to know that there are two different memory pools that C++ programs draw from.

''malloc'' and ''free''

malloc and free can also used be used to allocate and deallocate storage. They are part of C and so are available in C++ as well. However, they are more cumbersome than new and delete introduced in C++, and their use is discouraged.

Returning Pointers from Functions

A function can return a pointer:

returned-pointer.cpp
/** Returning a pointer from a function. */
#include <iostream>
using namespace std;
 
char* someChar();
 
int main()
{
    char *foo;
 
    foo = someChar();
 
    cout << *foo << endl;
 
    return 0;
}
 
char* someChar()
{
    char *myCharPtr = new char;
 
    *myCharPtr = 'a';
 
    return myCharPtr;
}

However, the function must not return a pointer to a local variable in the function—because that local variable will cease to exist after the function terminates.

Only return a pointer to:

  • data that was passed to the function as an argument, or
  • dynamically allocated memory, or
  • NULL.
Gaddis-Pr9-15.cpp
// This program demonstrates a function that returns
// a pointer.
#include <iostream>
#include <cstdlib>   // For rand and srand
#include <ctime>     // For the time function
using namespace std;
 
// Function prototype
int *getRandomNumbers(int);
 
int main()
{
   int *numbers;  // To point to the numbers
 
   // Get an array of five random numbers.
   numbers = getRandomNumbers(5);
 
   // Display the numbers.
   for (int count = 0; count < 5; count++)
      cout << numbers[count] << endl;
 
   // Free the memory.
   delete [] numbers;
   numbers = 0;
   return 0;
}
 
//**************************************************
// The getRandomNumbers function returns a pointer *
// to an array of random integers. The parameter   *
// indicates the number of numbers requested.      *
//**************************************************
 
int *getRandomNumbers(int num)
{
   int *arr = nullptr;	// Array to hold the numbers
 
   // Return null if num is zero or negative.
   if (num <= 0)
      return NULL;
 
   // Dynamically allocate the array.
   arr = new int[num];
 
   // Seed the random number generator by passing
   // the return value of time(0) to srand.
   srand( time(0) );
 
   // Populate the array with random numbers.
   for (int count = 0; count < num; count++)
      arr[count] = rand();
 
   // Return a pointer to the array.
   return arr;
}
cplusplus/pointers_4.1458599721.txt.gz · Last modified: 2016/03/21 22:35 by mithat

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki