Proper stack and heap usage in C++?

57,596

Solution 1

No, the difference between stack and heap isn't performance. It's lifespan: any local variable inside a function (anything you do not malloc() or new) lives on the stack. It goes away when you return from the function. If you want something to live longer than the function that declared it, you must allocate it on the heap.

class Thingy;

Thingy* foo( ) 
{
  int a; // this int lives on the stack
  Thingy B; // this thingy lives on the stack and will be deleted when we return from foo
  Thingy *pointerToB = &B; // this points to an address on the stack
  Thingy *pointerToC = new Thingy(); // this makes a Thingy on the heap.
                                     // pointerToC contains its address.

  // this is safe: C lives on the heap and outlives foo().
  // Whoever you pass this to must remember to delete it!
  return pointerToC;

  // this is NOT SAFE: B lives on the stack and will be deleted when foo() returns. 
  // whoever uses this returned pointer will probably cause a crash!
  return pointerToB;
}

For a clearer understanding of what the stack is, come at it from the other end -- rather than try to understand what the stack does in terms of a high level language, look up "call stack" and "calling convention" and see what the machine really does when you call a function. Computer memory is just a series of addresses; "heap" and "stack" are inventions of the compiler.

Solution 2

I would say:

Store it on the stack, if you CAN.

Store it on the heap, if you NEED TO.

Therefore, prefer the stack to the heap. Some possible reasons that you can't store something on the stack are:

  • It's too big - on multithreaded programs on 32-bit OS, the stack has a small and fixed (at thread-creation time at least) size (typically just a few megs. This is so that you can create lots of threads without exhausting address space. For 64-bit programs, or single threaded (Linux anyway) programs, this is not a major issue. Under 32-bit Linux, single threaded programs usually use dynamic stacks which can keep growing until they reach the top of the heap.
  • You need to access it outside the scope of the original stack frame - this is really the main reason.

It is possible, with sensible compilers, to allocate non-fixed size objects on the heap (usually arrays whose size is not known at compile time).

Solution 3

It's more subtle than the other answers suggest. There is no absolute divide between data on the stack and data on the heap based on how you declare it. For example:

std::vector<int> v(10);

In the body of a function, that declares a vector (dynamic array) of ten integers on the stack. But the storage managed by the vector is not on the stack.

Ah, but (the other answers suggest) the lifetime of that storage is bounded by the lifetime of the vector itself, which here is stack-based, so it makes no difference how it's implemented - we can only treat it as a stack-based object with value semantics.

Not so. Suppose the function was:

void GetSomeNumbers(std::vector<int> &result)
{
    std::vector<int> v(10);

    // fill v with numbers

    result.swap(v);
}

So anything with a swap function (and any complex value type should have one) can serve as a kind of rebindable reference to some heap data, under a system which guarantees a single owner of that data.

Therefore the modern C++ approach is to never store the address of heap data in naked local pointer variables. All heap allocations must be hidden inside classes.

If you do that, you can think of all variables in your program as if they were simple value types, and forget about the heap altogether (except when writing a new value-like wrapper class for some heap data, which ought to be unusual).

You merely have to retain one special bit of knowledge to help you optimise: where possible, instead of assigning one variable to another like this:

a = b;

swap them like this:

a.swap(b);

because it's much faster and it doesn't throw exceptions. The only requirement is that you don't need b to continue to hold the same value (it's going to get a's value instead, which would be trashed in a = b).

The downside is that this approach forces you to return values from functions via output parameters instead of the actual return value. But they're fixing that in C++0x with rvalue references.

In the most complicated situations of all, you would take this idea to the general extreme and use a smart pointer class such as shared_ptr which is already in tr1. (Although I'd argue that if you seem to need it, you've possibly moved outside Standard C++'s sweet spot of applicability.)

Solution 4

You also would store an item on the heap if it needs to be used outside the scope of the function in which it is created. One idiom used with stack objects is called RAII - this involves using the stack based object as a wrapper for a resource, when the object is destroyed, the resource would be cleaned up. Stack based objects are easier to keep track of when you might be throwing exceptions - you don't need to concern yourself with deleting a heap based object in an exception handler. This is why raw pointers are not normally used in modern C++, you would use a smart pointer which can be a stack based wrapper for a raw pointer to a heap based object.

Solution 5

To add to the other answers, it can also be about performance, at least a little bit. Not that you should worry about this unless it's relevant for you, but:

Allocating in the heap requires finding a tracking a block of memory, which is not a constant-time operation (and takes some cycles and overhead). This can get slower as memory becomes fragmented, and/or you're getting close to using 100% of your address space. On the other hand, stack allocations are constant-time, basically "free" operations.

Another thing to consider (again, really only important if it becomes an issue) is that typically the stack size is fixed, and can be much lower than the heap size. So if you're allocating large objects or many small objects, you probably want to use the heap; if you run out of stack space, the runtime will throw the site titular exception. Not usually a big deal, but another thing to consider.

Share:
57,596
Alexander
Author by

Alexander

Just another CS student.

Updated on June 14, 2021

Comments

  • Alexander
    Alexander almost 3 years

    I've been programming for a while but It's been mostly Java and C#. I've never actually had to manage memory on my own. I recently began programming in C++ and I'm a little confused as to when I should store things on the stack and when to store them on the heap.

    My understanding is that variables which are accessed very frequently should be stored on the stack and objects, rarely used variables, and large data structures should all be stored on the heap. Is this correct or am I incorrect?

  • Abhi
    Abhi about 15 years
    It'd be safe to add that variably sized information generally goes on the heap. The only exceptions I'm aware of are VLA's in C99 (which has limited support) and the alloca() function which is often misunderstood even by C programmers.
  • Steve Rowe
    Steve Rowe about 15 years
    I suspect he was asking when to put things on the heap, not how.
  • peterchen
    peterchen about 15 years
    Good explanation, though in a multithreaded scenario with frequent allocations and/or deallocations, heap is a point of contention, thus affecting performance. Still, Scope is almost always the deciding factor.
  • Crashworks
    Crashworks about 15 years
    Sure, and new/malloc() is itself a slow operation, and stack is more likely to be in dcache than an arbitrary heap line. These are real considerations, but usually secondary to the question of lifespan.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas about 15 years
    That is something I would not concern a user with at the beginning. For the user, vectors and lists seem to be allocated on the stack even if ths STL does store the contents on the heap. Question seemed more on the line of deciding when to explicitly call new/delete.
  • Mr.Ree
    Mr.Ree about 15 years
    Dan: I've put 2 gigs (Yes, G as in GIGS) onto the stack under 32bit linux. Stack limits are OS dependent.
  • Mr.Ree
    Mr.Ree about 15 years
    Both heap & stack are paged virtual memory. The heap search time is blazingly fast compared to what it takes to map in new memory. Under 32bit Linux, I can put >2gig onto my stack. Under Macs, I think the stack is hard-limited to 65Meg.
  • Ant
    Ant over 14 years
    mrree: The Nintendo DS stack is 16 kilobytes. Some stack limits are hardware dependent.
  • Vineeth Chitteti
    Vineeth Chitteti over 9 years
    Is it true "Computer memory is just a series of addresses; "heap" and "stack" are inventions of the compile" ?? I've read at many places that stack is a special region of our computer's memory.
  • tsturzl
    tsturzl about 9 years
    @kai That's a way to visualize it, but isn't necessarily true physically speaking. The OS is responsible for allocating the stack and heap of an application. The compiler is also responsible, but primarily it relies on the OS to do so. Stack is limited, and heap is not. This is due to the way the OS handles sorting these memory addresses into something more structured so that multiple applications can run on the same system. Heap and stack aren't the only ones, but they're typically the only two that most developers are concerned about.
  • underscore_d
    underscore_d over 8 years
    For reference of other readers: (A) The "should" here is purely the user's personal opinion, drawn from at best 1 citation and 1 scenario that many users are unlikely to encounter (recursion). Also, (B) the standard library provides std::unique_ptr, which should be preferred to any external library like Boost (though that does feed things to the standard over time).
  • underscore_d
    underscore_d over 8 years
    The link appears to be dead, but checking the Internet Archive Wayback Machine indicates that it talks only about the stack and therefore does nothing to answer the specific question here of stack versus heap. -1
  • user3130012
    user3130012 almost 8 years
    why would you return *pointertoB? i'm asking to see if there is a situation where you have no choice but to do that. if it was me in that situation i would change return type to a non pointer and return Thingy B. who ever gets the return usually has a variable to copy the result of foo(), like Thingy thingyResult= foo();
  • Viliami
    Viliami almost 8 years
    Ant: All stacks are hardware dependent, OS dependent, and also compiler dependent.
  • ArmaGeddON
    ArmaGeddON over 6 years
    "heap" and "stack" are inventions of the compiler. Can't believe I was living in dreams so far