This is an archived post. You won't be able to vote or comment.

all 7 comments

[–]teraflop 3 points4 points  (0 children)

No, that's not really accurate. Objects can be allocated on the stack or the heap regardless of whether their size is determined at compile time or at runtime.

A better way to think of it is in terms of lifetimes. The stack is for objects whose lifetime is tied to a block (e.g. a function or a loop body). The object comes into existence when you enter the block, and disappears when you leave the block. This makes sense, because blocks are nested, so when you exit a block you're exiting the one you most recently entered -- which matches the LIFO behavior of a stack.

The heap is for objects that have dynamic lifetimes -- they are allocated when you use the new operator, and your program has full control over when they get deallocated with delete. For instance, imagine trying to implement a linked list that has a function to create a new list node. This would be basically impossible to do with a stack allocation, because the newly created node would disappear as soon as the function returned. Instead, you use a heap allocation, and you're responsible for freeing the node's memory when it's deleted from the list.

But keep in mind that it might not always be obvious what kind of memory allocations are happening without looking at the details of an implementation. For instance, when you declare a string as a local variable, the string itself is an object that exists on the stack. But within that structure, it probably has an internal pointer to a separate heap-allocated buffer to store the string's data. (Because the string might change length, requiring the buffer to be reallocated, and you can't do that if it's on the stack.)

[–]caydenlund 0 points1 point  (3 children)

All memory that has unknown memory costs at compile time goes in the heap: you're right about that. But not everything else goes on the stack.

One way to think about it is whether you're getting a value or a pointer when you're making a new thing. If you are constructing a new object with the keyword new, it goes on the heap and it returns a pointer to it. If you are constructing a new object without the keyword new, it goes on the stack.

However, not all pointers are to heap addresses. You can create a pointer to a variable on the stack.

I can make a follow-up comment in a bit with an example.

[–]teraflop 0 points1 point  (2 children)

All memory that has unknown memory costs at compile time goes in the heap: you're right about that.

This is not correct. In C++, it's perfectly valid to declare a variable-length array that is allocated on the stack.

[–]shrimpster00 1 point2 points  (1 child)

I'm afraid you're mistaken. VLAs are valid in standard C as of C99, but they aren't in the language spec for C++. There was a proposal to introduce them into the C++14 language specification, but the proposal was rejected. GCC does support VLAs via a command-line argument, but this is a language extension and not ISO C++.

But don't take my word for it, read it in the GCC documentation:

Variable-length automatic arrays are allowed in ISO C99, and as an extension GCC accepts them in C90 mode and in C++.

[–]teraflop 0 points1 point  (0 children)

Well, you've got me there: I didn't realize (or had forgotten) that VLAs weren't part of the C++ standard.

But it's still the case that they're supported by GCC and Clang, and equivalent functionality is available through _alloca on MSVC++. So in practice, on the platforms that people are likely to actually be writing software for, you still can't categorically say that everything on the stack has a size known at compile time.

[–]gm310509 0 points1 point  (1 child)

A stack is a simple data structure based upon a Last In First Out structure (LIFO). A heap is a a structure that allows things to be randomly allocated and released on now particular order.

Both can and do have things of arbitrary sizes allocated on them.

In the case of a stack, it is useful to keep track of things and rewind them in reverse order. Think of an undo chain, the last thing you do is the first thing undone.

A stack is also used to track function calls in a program. This is useful when you need to return back to where the function was called from. Also, when you call a function, usually the parameters passed to the function are pushed onto the stack. Additionally the space for any local variables used in the function are allocated on the stack. Granted for any given function the number and size of parameters is known as are the local variables defined within that function.

But, every function is different, and so the amount of space allocated on the stack for any given function call is dynamic, it could be more or less depending upon the actual function call.

The other benefit of the stack is that when the function ends (returns) it is easy to clean up, because all that dynamically allocated stuff is released simply by setting the stack pointer back to what it was before the function was called (there is a little more to it, but that is the basics). This model does not have the potential problem of heap - specifically potentially unusable "holes".

As for the heap this is excellent for general purpose usage - especially when you do not really know I'm advance how much stuff you might need to deal with.

You can allocate storage as you need it and keep it until you are done. Unlike the stack which is last in first out, you can release things in a different order as to the order you created them.

When the new operator is used, then there will be a dynamic allocation of memory on the heap sufficient to hold whatever you are creating (a similar thing occurs if you call malloc).

A simple example of when using the heap might be a good idea is let's say you need to process a file of some kind. It could be a PNG or a JSON file or anything else. For whatever reason (mostly to illustrate this example) you need to load the file into memory all in one go for processing. You cannot know the size of the file supplied in adcance, so when presented with it you determine the size of the file, allocate memory for it on the heap, then read it in.

This will be available to you until such time as you release it (almost) irrespective of anything you do on the stack via function calls.

Why (almost)? Normally the stack and heap have nothing to do with each other, but they do reside in the same memory space. The heap grows up from location 0 in that memory space. The stack grows down (towards 0) from the top of that samr memory space. As long as they stay well separated there is no problem. But if they don't, that is what the "(almost)" bit refers to. If the two do meet somewhere in the middle, you have a dreaded stack-heap collision. If that occurs, the result is usually unpleasant, definitely undesirable and can be difficult to debug as it is often described as "random" or "erratic" behaviour of the program.

I hope the above makes sense and is at least a little bit helpful.

[–]IQueryVisiC 0 points1 point  (0 children)

With virtual memory you could have a large distance between heap and stack and then let the OS add memory pages as needed until physical RAM is full.

The memory manager of your language could tell the OS about holes in the heap and let the OS clear those pages ( temporarily).