views:

446

answers:

5

What are some of the technical differences between memory that is allocated with the new operator and memory that is allocated via a simple variable declaration, such as int var? Does c++ have any form of automatic memory management?

In particular, I have a couple questions. First, since with dynamic memory you have to declare a pointer to store the address of the actual memory you work with, doesn't dynamic memory use more memory? I don't see why the pointer is necessary at all unless you're declaring an array.

Secondly, if I were to make a simple function such as this:

int myfunc() { int x = 2; int y = 3; return x+y; }

...And call it, would the memory allocated by the function be freed as soon as it's scope of existence has ended? What about with dynamic memory?

+2  A: 

Dynamic memory lives on the heap as opposed to the stack. The lifetime of dynamic memory is from the time of allocation, to the time of deallocation. With local variables, their lifetime is limited to the function / block they are defined in.

Regarding your question about the memory usage in the function, in your example the memory for your local variables would be freed at the end of the function. However, if the memory was dynamically allocated with new, it would not be automatically disposed, and you would be responsible for explicitly using delete to free the memory.

Regarding automatic memory management, the C++ Standard Library provides auto_ptr for this.

Raul Agrait
+2  A: 

Memory allocated by "new" ends up on the heap.

Memory allocated in a function resides inside the function where the function is placed on the stack.

Read about stack vs heap allocation here: http://www-ee.eng.hawaii.edu/~tep/EE160/Book/chap14/subsection2.1.1.8.html

Fragsworth
A: 

Memory allocated with the new operator is fetched from a memory section called "heap" while static allocations for variables are use a memory section shared with procedure/function-calls (the "stack").

You only need to worry about the dynamic memory allocations you made yourself with new, variables which are known at compile-time (defined in the source) are automatically freed at the end of their scope (end of function/procedure, block, ...).

Kosi2801
A: 

The big difference between "dynamic" and "ordinary" memory was rather good reflected in the question itself.

Dynamic memory is not too good supported by C++ at all.

When you use dynamic memory, you are totally responsible for it by yourself. You have to allocate it. When you forget to do it and try to access it threw your pointer, you will have plenty off negative surprises. Also you have to free the memory -- and when you forget it by any way, you will have even more surprises. Such errors belong to the most difficult errors to find in C/C++ programms.

You need an extra pointer, since somehow you need access to your new memory. Some memory (if dynamic or not) is first of it nothing a programming language can handle. You need to have access to it. This is done by variables. But variables in languages like C++ are stored in "ordinary" memory. So you need to have "pointers" -- pointers are a form of indirection, that says "No, I am not the value you are searching for, but I point to it". Pointers are the only possibility in C++ to access dynamic memory.

By contrast, "ordinary" memory can be accessed directly, allocation and freeing is done automatically by the language itself.

Dynamic memory and pointers is the biggest source for problems in C++ -- but it is also a very mighty concept -- when you do it right, you can do much more then with ordinary memory.

That is also the reason, plenty of libraries have functions or whole modules for dealing with dynamic memory. The auto_ptr-example was also mentioned in a parallel answer, that tries to deal with the problem, that dynamic memory should be reliably released at the end of a method.

Normally you will use dynamic memory only in cases you really need it. You will not use it, to have a single integer variable, but to have arrays or build larger data structures in memory.

Juergen
+10  A: 

Note: This answer is way too long. I'll pare it down sometime. Meanwhile, comment if you can think of useful edits.


To answer your questions, we first need to define two areas of memory called the stack and the heap.

The stack

Imagine the stack as a stack of boxes. Each box represents the execution of a function. At the beginning, when main is called, there is one box sitting on the floor. Any local variables you define are in that box.

A simple example

int main(int argc, char * argv[])
{
    int a = 3;
    int b = 4;
    return a + b;
}

In this case, you have one box on the floor with the variables argc (an integer), argv (a pointer to a char array), a (an integer), and b (an integer).

More than one box

int main(int argc, char * argv[])
{
    int a = 3;
    int b = 4;
    return do_stuff(a, b);
}

int do_stuff(int a, int b)
{
    int c = a + b;
    c++;
    return c;
}

Now, you have a box on the floor (for main) with argc, argv, a, and b. On top of that box, you have another box (for do_stuff) with a, b, and c.

This example illustrates two interesting effects.

  1. As you probably know, a and b were passed-by-value. That's why there is a copy of those variables in the box for do_stuff.

  2. Notice that you don't have to free or delete or anything for these variables. When your function returns, the box for that function is destroyed.

Box overflow

    int main(int argc, char * argv[])
    {
        int a = 3;
        int b = 4;
        return do_stuff(a, b);
    }

    int do_stuff(int a, int b)
    {
        return do_stuff(a, b);
    }

Here, you have a box on the floor (for main, as before). Then, you have a box (for do_stuff) with a and b. Then, you have another box (for do_stuff calling itself), again with a and b. And then another. And soon, you have a stack overflow.

Summary of the stack

Think of the stack as a stack of boxes. Each box represents a function executing, and that box contains the local variables defined in that function. When the function returns, that box is destroyed.

More technical stuff

  • Each "box" is officially called a stack frame.
  • Ever notice how your variables have "random" default values? When an old stack frame is "destroyed", it just stops being relevant. It doesn't get zeroed out or anything like that. The next time a stack frame uses that section of memory, you see bits of old stack frame in your local variables.

The heap

This is where dynamic memory allocation comes into play.

Imagine the heap as an endless green meadow of memory. When you call malloc or new, a block of memory is allocated in the heap. You are given a pointer to access this block of memory.

int main(int argc, char * argv[])
{
    int * a = new int;
    return *a;
}

Here, a new integer's worth of memory is allocated on the heap. You get a pointer named a that points to that memory.

  • a is a local variable, and so it is in main's "box"

Rationale for dynamic memory allocation

Sure, using dynamically allocated memory seems to waste a few bytes here and there for pointers. However, there are things that you just can't (easily) do without dynamic memory allocation.

Returning an array

int main(int argc, char * argv[])
{
    int * intarray = create_array();
    return intarray[0];
}

int * create_array()
{
    int intarray[5];
    intarray[0] = 0;
    return intarray;
}

What happens here? You "return an array" in create_array. In actuality, you return a pointer, which just points to the part of the create_array "box" that contains the array. What happens when create_array returns? Its box is destroyed, and you can expect your array to become corrupt at any moment.

Instead, use dynamically allocated memory.

int main(int argc, char * argv[])
{
    int * intarray = create_array();
    int return_value = intarray[0];
    delete[] intarray;
    return return_value;
}

int * create_array()
{
    int * intarray = new int[5];
    intarray[0] = 0;
    return intarray;
}

Because function returning does not modify the heap, your precious intarray escapes unscathed. Remember to delete[] it after you're done though.

Wesley