When Is It Best to Use the Stack Instead of the Heap and Vice Versa

When is it best to use the stack instead of the heap and vice versa?

Use the stack when your variable will not be used after the current function returns. Use the heap when the data in the variable is needed beyond the lifetime of the current function.

Why would you ever want to allocate memory on the heap rather than the stack?

There are a few reasons:

  • The main one is that with heap allocation, you have the most flexible control over the object's lifetime (from malloc/calloc to free);
  • Stack space is typically a more limited resource than heap space, at least in default configurations;
  • A failure to allocate heap space can be handled gracefully, whereas running out of stack space is often unrecoverable.

Without the flexible object lifetime, useful data structures such as binary trees and linked lists would be virtually impossible to write.

C best practices, stack vs heap allocation

You should use malloc if:

  1. You're passing pointers to non-static const data up the call stack, or
  2. you're allocating variable or simply large amounts of data (else you risk stack overflow).

In other cases, stack allocation should be fine.

Proper stack and heap usage in C++?

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.

Is it a bad manner to use the stack to avoid dynamic allocation?

The question of allocation in stack (or) heap is dependent on the following parameters:

  • The scope and life time in which you want the variable to be available: Say in your program if you need to use members of the array of double after test_alloc in main, you should go for dynamic allocation .

  • The size of your stack: Say you are using the test_alloc function in a context of a thread which has a stack of 4K, and your array size which in in turn dependent on the size variable accounts to 1K, then stack allocation is not advisable, you should go for dynamic allocation.

  • Complexity of code: While using dynamic allocations, enusre to take care of avoiding memory leaks, dangling pointers etc. This adds some amount of complexity to the code and are more prone to bugs.

  • The frequency of allocation: Calling malloc and free in a loop may have lesser performance. This point is based on the fact that allocation in stack is just the matter of offseting the SP, on contrary while allocation in heap needs some more book keeping.

Objects on a stack vs Objects on a heap in C++

Objects on the stack have the really neat property that the memory that backs them is automatically freed at the end of that stack frame (e.g. when the function returns.) C++ extends this concept by also calling destructors for all stack objects whenever they fall out of scope (i.e. they're still freed in the case that an exception is thrown before the function returns.) Since this makes memory management dead simple, and memory management errors have the frustrating combination of being easy to make and hard to detect, stack allocation should be preferred whenever it is feasible.

The downside of stack-allocated objects is...well...they're deleted when the function returns. Sometimes there are legitimate reasons to want the object to live longer. In those cases, you have no choice but to allocate from the heap.

Another point to consider is that stack allocation pretty much has to be a size that is known at the time the software is compiled (but see alloca function available on some platforms.) There are a ton of real world scenarios where you won't know until the program is running how much memory you need. Take for example an address book application. If I am coding such an application, I obviously have no idea how many people the end user is going to want in their address book. The user has to tell the program this information. In this case, you need to have dynamically allocated memory, so you're again looking at heap allocation.

Can the stack grow into the heap?

The much-simplified view of memory traditionally looks something like this:

 ===================
| Operating System | High memory
===================
| Your program |
| --------------- |
| | Process stack | |
| --------------- | Transient program area
| | Process heap | |
| --------------- |
| | Program code | |
| --------------- |
===================
| Operating system | Low memory
===================

As you pointed out, the process stack starts just below the operating system code, and grows downward. The process heap, on the other hand, starts just above the fixed program code, and grows upward.

In the early days of PC operating systems that really was the physical layout of a program in memory. The CP/M operating system, for example, reserved the first 256 bytes of memory for some operating system bootstrap code, and the rest of the necessary operating system services occupied the high memory area. Programs started at address 0x0100, and could use all the memory between there and the start of the operating system code at the top. MS-DOS was very similar.

There were no guardrails to prevent what you mentioned from happening: a program would allocate so much space on the stack that it overwrote memory that was allocated on the heap. Or, the program would allocate heap memory that overwrote the processor stack. When either of those things happened, the program would crash. In some cases, the operating system would crash, too. Of course, because only one program could be running at a time, that wasn't a huge deal: just reboot the machine and try again.

Modern computers have much more advanced memory layouts, and that conceptual picture no longer holds true. Operating systems today can do a much better job of enforcing memory access restrictions. A process, for example, is assigned a fixed segment for its stack (typically on the order of 1 megabyte). If the program tries to use more stack space than is allocated, the memory manager won't allow it. The program will crash with an access violation. And the program's heap can't grow into the memory allocated to the stack for the same reason.



Related Topics



Leave a reply



Submit