stack memory --- automatic local variables (the default) global memory --- global variables and static local variables heap memory --- dynamically sized and allocated variables (by new in C++)
Stack memory is used for all local variables defined the "usual" way, including arrays. At run time, each call to a function, including main(), causes a chunk of memory (called an activation record) associated with that function, its arguments, and its automatic local variables, to be placed on the program's run-time stack. The precise locations are determined at the time the function is actually called. Once execution of that function finishes, that stack storage is recycled for use by subsequent function calls, either to the same function or to other functions.
For example, all variables in the following code will go on the program stack at run time. The data[] array goes on the stack only once: in the chunk used by the main() function. The printArray() function, when called by main(), shares the main() function's storage for the array.
void printArray( int a[], int length ){ for (int i = 0; i < length; ++i) cout << a[i] << ' '; cout << endl; } int main(){ int data[] = {5,7,9,11,13}; // Note: here, the array size is determined printArray( data, 5 ); // by number of initial data. return 0; }
Occasionally, one wants a function to save or "remember" certain results from one call to the next. You could use a global variable to save the info, but that's bad style, as other functions might accidentally access it. Alternatively, you can specify that a local variable be stored in the global memory space, so that it doesn't go on the stack and therefore doesn't get destroyed/recycled each time its function finishes and returns control to the caller. To put a local variable in the global memory space, use the keyword static, as illustrated below.
Example: a function which returns the number of times it has been called, without use of a global variable.
int beenCalled() { static int n = 0; return ++n; }Note: in this example, n is initialized only once, at the beginning of the program. The output of the following program is "123".
int main(){ for (int i=0; i<3; ++i) cout << beenCalled(); cout << endl; return 0; }
Because a static local variable is stored in the global memory space, its storage doesn't get recycled when its function finishes. It retains its storage and value from one function call to the next. A static variable isn't const; its value can be changed. What distinguishes it from the usual, automatic local variables is that its storage location doesn't change from one function call to the next. Its lifetime isn't limited to the execution of the function containing it. Rather, its lifetime is the duration of the entire program. Therefore, its initialization is done only once --- at compile time, or at the beginning of its program's execution, or the first time its function is called (it doesn't matter which) --- not every time its function is called.
A variable or array allocated with new must be deallocated with delete to prevent a memory leak. In C++, such a variable has no name, and therefore has no scope. It is accessed through a pointer. Although the pointer has a name and scope, the memory it points to doesn't. The lifetime of the newly dynamically allocated memory is determined completely by what happens in between its creation with new and its destruction with delete. The storage used for such an object is taken from a special 3rd area of memory called the heap or free store.
As there are three main kinds of memory used by a process (executing program), there are potentially three ways a program may run out of memory --- global, stack, and heap.
Running out of global memory space is possible if you try to create a global or static array or other static data structure that's bigger than will fit into the main memory (part of RAM) available to your program. This might happen if you build a program on one machine and then try to run it on another machine with less memory. Obviously, this type of error doesn't often happen with small programs or small amounts of data.
Stack memory may be depleted in a couple of ways. First, creating very large arrays as local variables may overfill the stack. If a local array is large, make it static to avoid this problem. Second, if your program generates a long sequence of function calls, each requiring substantial storage, e.g., if a function "calls itself" many times, then the sequence of calls might require more stack memory than is available, because all the calls have to be separately and simultaneously stored on the stack. Most systems give the programmer a straightforward way of increasing the amount of stack memory available to a process. Usually, you can also just rewrite your code to avoid using so much of the stack at the same time. E.g., use a loop instead of a function calling itself.
For programs written in C++, the most problematic kind of memory failure is a failure of the new operator to acquire heap memory. (You can detect this type of failure by checking the value of the address returned by the new operator. A NULL (0) value indicates a failure; non-null indicates success.) This type of failure is usually caused by a memory leak, i.e., the failure of your program to release memory back to the heap with delete or delete [] after it has finished using it. Although when your program finishes executing, the operating system automatically reclaims any memory you forgot to release, your program may allocate memory inside loops or other recursions that execute many, many times before your program finishes just one execution. Thus, your program may in some situations gobble up the entire heap before it finishes its task. It's easier to do than you might think.
To avoid this very dangerous memory-leak error, you must make sure that whenever you create/acquire an object with new, you immediately ensure that it will later be destroyed/released with delete. The surest way to do this is to limit your use of these operators (new/delete) to within the automatically called constructors and destructors of user-defined or library-defined types (e.g., STL types like vector). The only other time I would recommend using new or delete is if the same function that allocates with new later deallocates with delete (when I type the code for such a function, I always type the delete statement immediately after typing its corresponding new statement, then type the code in between).
Never try to transfer the responsibility for deallocation from one module to another, unless both modules are part of a single type definition, for which correct memory management is assured.
Finally, if several functions share the same dynamically allocated object, and any one of them may resize it (by deallocating it, then reallocating a larger version of it), then they should all share the storage for the pointer to it as well. When that pointer is passed from one such function to another, it must be passed by reference (e.g., int *&) to ensure that all the functions maintain their access. See the file search example online.