Question

The way I've been taught for quite some time is that when I run a program, the first thing that immediately goes on the stack is a stack frame for the main method. And if I call on a function called foo() from within main, then a stack frame that is the size of the local variables ( automatic objects) and the parameters gets pushed onto the stack as well.

However, I've ran into a couple things that contradict this. And I'm hoping someone can clear up my confusion or explain why there really aren't any contradictions.

First contradiction:

In the book, "The C++ Programming Language" 3rd edition by Bjarne Stroustrup, it says on page 244, "A named automatic object is created each time its declaration is encountered in the execution of the program." If that's not clear enough, on the next page it says, "The constructor for a local variable is executed each time the thread of control passes through the declaration of the local variable."

Does this mean that the total memory for a stack frame is not allocated all at once, but rather block by block as the variable declarations are encountered ? Also, does this mean that a stack frame may not be the same size every time if a variable declaration was not encountered due to an if statement ?

Second contradiction:

I've done a little coding in assembly ( ARM to be specific ), and the way my class was taught was that when a function was called, we immediately used the registers and never pushed any of the local variables of the current function onto the stack unless the algorithm was not possible to perform with the limited amount of registers. And even then, we only pushed the leftover variables.

Does this mean when a function is called, a stack frame may not be created at all ? Does this also imply that a stack frame may differ in size due to the use of registers ?

Was it helpful?

Solution

Regarding your first question:

The creation of the object has nothing to do with the allocation of the data itself. To be more specific: the fact that the object has its reserved space on the stack doesn't imply anything about when its constructor is called.

Does this mean that the total memory for a stack frame is not allocated all at once, but rather block by block as the variable declarations are encountered?

This question is really compiler specific. A stack pointer is just a pointer, how it is used by the binary is up to the compiler. Actually some compilers may reserve the whole activation record, some may reserve just little by little, some other may reserve it dynamically according to the specific invocation and so on. This is even tightly coupled with optimization so that the compiler is able to arrange things in the way it thinks is better.

Does this mean when a function is called, a stack frame may not be created at all ? Does this also imply that a stack frame may differ in size due to the use of registers ?

Again, there is no strict answer here. Usually compilers rely on register allocation algorithms that are able to allocate registers in a way that minimizes "spilled" (on stack) variables. Of course, if you are writing in assembly by hand, you can decide to assign specific registers to specific variables throughout your program just because you know by their content how you want to make it work.

A compiler can't guess this, but it can see when a variable starts to be used or is no longer needed and arrange things in a way that minimize memory accesses (so stack size). For example, it could implement a policy such that some registers should be saved by the called, some others by the callee and assign or whatever.

OTHER TIPS

  1. Constructing a C++ object has very little to do with acquiring memory for the object. In fact, it would be more accurate to say "reserving memory", since in general, computers do not have little teams of RAM-builders which spring into action every time you ask for a new object. The memory is more or less permanent (although we could quibble about VM). Of course, the compiler has to arrange for its program to only use a particular range of memory for one thing at a time. That may (and probably does) require it to reserve a range of memory prior to the object's existence, and avoid using it for other objects until some time after the object's disappearance. For efficiency, the compiler may (even in the case of objects with dynamic storage duration) optimize reservations by reserving several blocks of memory at once, if it knows it will need them. In any event, when C++ talks about "constructing an object", it means just that: taking a range of memory with undefined contents, and doing what is necessary to create the representation of the object (and whatever else in the state of the world is implied by the creation of the object, which might not be limited to a particular hunk of memory.)

  2. There is no requirement for stack frames to exist. There is no requirement for a stack to exist. That's all up to the compiler. Most compilers do generate code which uses a stack, of course, and good compilers will figure out when it is possible to abbreviate or even omit a stack frame. So, yes, frames may vary in size.

You are absolutely right, a stack frame is not required. Stack frames are a quick and dirty solution to the problem of managing the local space, easier to debug than to manage changes in the stack pointer during the course of the function. If there is a need for the stack within the function it is easier to just adjust the stack pointer on entry and restore it on return.

This is also not black and white, compilers are programs like any other program, and if you dont already know then you will come to realize that given any number of programmers you are going to get multiple solutions to the same problem. Even if the number of programmers is one that one person may choose to solve the problem over and over again until they are satisfied and/or for whatever reason may choose to release the various versions. The use of the stack is very common for local variables, it is really how you do it but that does not mean you have to use a stack frame created on entry and restored on return.

And as you have learned in your classes and is very easy to see through experiments (compile some simple functions, with various levels of optimization from no optimization to some) that for example gcc wont use the stack unless it has to. We are talking arm here right where the normal calling convention is register based (there is nothing that says the compiler author(s) have to follow that convention, it is possible to use stack based on arm if a compiler choose to do that). Processors where the normal convention is stack based since the code is already dealing with the stack it may choose to use a stack frame anyway. It is likely that in those cases the stack based convention is used because the processor lacks general purpose registers and relies more on the stack than other processors with more registers, which means that processor likely needs the stack often not just for the calling convention but for most of the local storage.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top