2

Sometimes, I see that there is a mix of concepts between the duration of the storage and where does this occur. That is because sometimes I've seen the following statement:

int i; // This is in the stack!
int* j = new int; // This is in the heap!

But is this really true 100% of the time? Does C++ ensure where the storage takes place? Or, is it decided by the compiler?

Is the location of the storage independent from the duration?

For example, taking those two snippets:

void something()
{
   int i;
   std::cout << "i is " << i << std::endl;
}

vs:

void something()
{
   int* i = new int;
   std::cout << "i is " << i << std::endl;
   delete i;
}

Both are more or less equivalent regarding the lifetime of i, which is created at the begining and deleted at the end of the block, here the compiler could just use the stack (I don't know!), and the opposite could happen too:

void something()
{
   int n[100000000]; // Man this is big
}

vs:

void something()
{
  int* n = new int[100000000];
  delete n;
}

Those two cases should be in the heap to avoid stack-overflow (Or at least is what I've been told so far...), does the compiler that also that into account, besides the storage duration?

Sigma Octantis
  • 903
  • 1
  • 8
  • 25
  • The concept of stack/heap is not part of the C++ standard. Also thinking about objects in these terms is not helpful (think about an int member of a class that is dynamically allocated). Think about objects in terms of storage duration only. – Martin York Dec 29 '19 at 13:16
  • @MartinYork Thinking about stack/heap can be useful or necessary, even if it isn't part of the standard. For example, the `int n[100000000];` example would probably cause stack overflow on most systems. – interjay Dec 29 '19 at 13:26
  • Are you asking about how is it internally implemented? The `new` operator may be implemented in many ways, it can also use memory allocated the stack. – KamilCuk Dec 29 '19 at 13:34
  • 1
    In common implementations (like G++ and Visual Studio), variable definitions outside of a function are not allocated on the stack. They are allocated in a memory section that is neither stack nor heap. The area is usually where all global variables are placed. Often, this memory area is allocated by the executable format (e.g. predefined locations). – Thomas Matthews Dec 29 '19 at 19:03
  • @interjay which is why a valid implementatio that has those limitations may not even use the heap for that variable. So doubly underscoringmy point about thinking about these things is harmefull. – Martin York Dec 31 '19 at 13:10
  • @MartinYork Do you know any implementation that would convert `int n[100000000];` to a heap allocation? Because on implementations I've used that would cause a stack overflow. So if you want your program to work on a realistic implementation, it's important to know the difference between stack and heap. – interjay Dec 31 '19 at 14:38
  • @interjay I would say its more important to improve the compiler to be standard compliant. – Martin York Jan 02 '20 at 19:20
  • @MartinYork Are you seriously claiming that a compiler with a limited stack space is not standards-compliant? Maybe you should file bugs on GCC, clang, and pretty much all other compilers then. – interjay Jan 02 '20 at 19:55
  • @interjay Why are you making straw man arguments? – Martin York Jan 02 '20 at 22:06
  • If a compiler has a limitation it merely has to document that limitation. But an easy alternative is to allow the compiler to use multiple appropriate depending on the context. This is how we get better compilers over time. There is no requirement that an array be implemented on the heap. – Martin York Jan 02 '20 at 22:10
  • [Is there a max array length limit in C++?](https://stackoverflow.com/a/216731/14065) – Martin York Jan 02 '20 at 22:13
  • @MartinYork You said "its more important to improve the compiler to be standard compliant.". I don't see any other way to interpret that than that you think that stack size limitations are not standard compliant. No straw man here. Your answer that you linked is exactly what I've been saying, so I don't understand why (or what) you're arguing here. I'm done with this discussion as I can't make any sense of it. – interjay Jan 02 '20 at 22:23

2 Answers2

3

Is the location of the storage independent from the duration?

A0: Duration specifies expected/required behavior.
A1: The standard does not specify how that is implemented.
A2: The standard does not even require a heap or stack!

void something()
{
   int i;
   std::cout << "i is " << i << std::endl;
}

void something()
{
   int* i = new int;
   std::cout << "i is " << i << std::endl;
   delete i;
}

In the first example you have "automatic" storage duration and the second case is "dynamic" storage duration. The difference is that "automatic" will always be destroyed at the end of scope while the second will only be destroyed if the delete is executed.

Where the objects are created is not specified by the standard and completely left to the implementing.

On implementations that use an underlying heap that would be an easy implementation choice for the first example; but not a requirement. The implementation can quite easily call the OS for dynamic memory for the space required for the integer and still behave like the standard defines as long as the code to release the memory is also planted and executed when the object goes out of scope.

Conversely the easy way to implement the dynamic storage duration (second example) is to allocate memory from the runtime and then release it (assuming your implementation has this ability) when you hit the delete. But this is not a requirement. If the compiler can prove that there are not exceptions and you will always hit the delete then it could just as easily put it on the heap and destroy it normally. NOTE: If the compiler determines that the object is always leaked. It could still put it on the heap and simply not destroy it when it goes out fo scope (that is a perfectly valid implementation).

The second set of examples adds some complications:

Code:

int n[100000000]; // Man this is big

This is indeed very large. Some implementations may not be able to support this on a stack (the stack frame size may be limited by the OS or hardware or compiler).

A perfectly valid implementation is to dynamically allocate the memory for this and ensure that the memory is released when the object goes out of scope.

Another implementation is to simply pre-allocate the memory not on the stack but in the bzz (going from memory here. This an assembler zone of an application that stores memory). As long as it implements the expected behavior of calling any destructors at the end of scope (I know int does not have a destructor so it makes that easy).

Martin York
  • 257,169
  • 86
  • 333
  • 562
1

Does C++ ensure where the storage takes place? Or it is decided by the compiler?

When you declare a variable like:

int i;

It has automatic storage. It could indeed be on the stack, but it's also common to just allocate a register for it, if enough registers are available. Theoretically it is also valid for the compiler to allocate heap memory for this variable, but in practice this does not happen.

When you use new, it is actually up to the standard library to allocate the memory for you. By defeault, it will use the heap. However, it could in theory also allocate the memory on the stack, but of course this would normally be the wrong thing to do, as any stack storage disappears when you return from the function where you called new.

In fact, new is just an operator, like +, and you can overload it. Typically, you would overload it inside a class, but you can also overload the global new operator (and similarly, the delete operator), and have it allocate storage from whereever you want.

Is the location of the storage independent from the duration?

In principle yes, but in practice automatic variables that only have the lifetime of the duration of a function are placed on the stack, whereas data you allocate with new is usually intended to outlive the function that called it, and that goes on the heap.

Those two cases should be in the heap to avoid stack-overflow (Or at least is what I've been told so far...), does the compiler that also that into account, besides the storage duration?

GCC and Clang never use heap allocation for variables with automatic storage as far as I can tell, regardless of their size. So you have to either use new and delete yourself, or use a container that manages the storage for you. Some containers, like std::string, will avoid heap allocations if you only store a small number of elements in them.

G. Sliepen
  • 7,637
  • 1
  • 15
  • 31
  • 1
    Note: putting `int n[100000000];` is an easy way to break the stack. Some implementation have a limited size of stack frame and that object would not fit in the stack frame limit. To implement this correctly the compiler would need to dynamically allocate this on these systems (or issue a warning that an implementation limit had been exceeded). – Martin York Dec 29 '19 at 13:36