9

Suppose we have the following:

void print()
{
     int a;  // declaration
     a = 9;
     cout << a << endl;
}

int main ()
{
     print();
}

Is the storage for variable a allocated at the moment function print is called in main or is it when execution reaches the declaration inside the function?

Brandon Tiqui
  • 1,399
  • 3
  • 15
  • 32
  • I assume you are talking about storage on the stack, not in a register, right? – Ponkadoodle Mar 01 '10 at 05:14
  • 1
    This is not defined. It is completely upto the compiler. The only thing defined is the lifetime of the object. It may not even have any memory allocated at all. – Martin York Mar 01 '10 at 06:11

5 Answers5

9

This is very much compiler dependent under the covers, but logically the storage is assigned as soon as the variable is declared.

Consider this simplistic C++ example:

// junk.c++
int addtwo(int a)
{
    int x = 2;

    return a + x;
}

When GCC compiles this, the following code is generated (; comments mine):

.file   "junk.c++"
    .text
.globl _Z6addtwoi
    .type   _Z6addtwoi, @function
_Z6addtwoi:
.LFB2:
    pushl   %ebp           ;store the old stack frame (caller's parameters and locals)
.LCFI0:
    movl    %esp, %ebp     ;set up the base pointer for our parameters and locals
.LCFI1:
    subl    $16, %esp      ;leave room for local variables on the stack
.LCFI2:
    movl    $2, -4(%ebp)   ;store the 2 in "x" (-4 offset from the base pointer)
    movl    -4(%ebp), %edx ;put "x" into the DX register
    movl    8(%ebp), %eax  ;put "a" (+8 offset from base pointer) into AX register
    addl    %edx, %eax     ;add the two together, storing the results in AX
    leave                  ;tear down the stack frame, no more locals or parameters
    ret                    ;exit the function, result is returned in AX by convention
.LFE2:
    .size   _Z6addtwoi, .-_Z6addtwoi
    .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
    .section    .note.GNU-stack,"",@progbits

Everything between _Z6addtwoi and .LCFI2 is boilerplate code used to set up the stack frame (store the previous function's variables, etc. safely out of the way). That last "subl $16, %esp" is the allocation of the local variable x.

.LCFI2 is the first bit of actual executing code that you've typed. "movl $2, -4(%ebp)" is putting the value 2 into the variable. (Initialization, in other words.) Now your space is allocated AND initialized. After that it loads the value into register EDX and follows that by moving your parameter, found in "8(%ebp)", into another register EAX. It then adds the two together, leaving the result in EAX. This is now the end of any code you've actually typed. The rest is again just boilerplate. Since GCC mandates that integers are returned in EAX, no work has to be done for the return value. The "leave" instruction tears down the stack frame and the "ret" instruction returns control back to the caller.

TL;DR summary: you can think of your space as having been allocated with the very first line of executable code in your block (paired {}).


I thought I'd clean this up a bit with explanatory comments seeing as this is the selected answer.

JUST MY correct OPINION
  • 33,835
  • 16
  • 75
  • 95
  • Actually, that should be "as soon as the variable is _defined_". Declarations do not reserve any storage: http://stackoverflow.com/questions/1410563/ – sbi Mar 01 '10 at 09:02
  • Can you even declare a local variable without defining it? And for globals, storage is allocated at program startup. – MSalters Mar 01 '10 at 10:02
  • Initialized globals are allocated *before* program startup with GCC. I just happened to have tested this recently by compiling a file with the following defined in global scope: static char c[100000000] = ""; The output file from GCC gets very large when you do stuff like that. Smarter compilers like CL will do allocation and initialization at startup. – JUST MY correct OPINION Mar 01 '10 at 10:09
3

As for construction of objects:

Construction happen at the point of declaration, and the destructor is called when the object goes out of scope.

But construction of objects and when memory is allocated do not need to coincide.

Just as destruction of objects and when memory is deallocated do not need to coincide.

As for when the memory on the stack is actually allocated:

I don't know, but you could check via the following code:

void f()
{
  int y;
  y = 0;//breakpoint here

  int x[1000000];
}

By running this code you can see where the exception happens, for me on Visual Studio 2008 it happens on entry of the function. It never reaches the breakpoint.

Brian R. Bondy
  • 314,085
  • 114
  • 576
  • 619
  • Your code doesn't demonstrate when it actually allocates the stack space, though, only when it runs the constructor. There's no reason those need to be the same. Also, there's no reason that it needs to deallocate the space for c at the end of that scope. It just needs to run the destructor. – Brooks Moses Mar 01 '10 at 05:28
  • @Brooks Moses: I think you made this comment just as I edited it to distinguish that exact thing. – Brian R. Bondy Mar 01 '10 at 05:29
  • Yup, looks like it, so I've upvoted you. :) I wrote a supplementary answer to show a different way of demonstrating this in code. (Of course, the best way is just to look at the compiled assembly, and see where it increments the stack pointer -- which I now see someone else has done just as I was making *this* comment; hah!) – Brooks Moses Mar 01 '10 at 05:40
3

As a supplement to Brian R. Bondy's answer: It's easy enough to run some experiments to show how this works, in a bit more detail than throwing out-of-stack-space errors. Consider this code:

#include<iostream>

void foo()
{
  int e; std::cout << "foo:e " << &e << std::endl;
}

int main()
{
  int a; std::cout << "a: " << &a << std::endl;
  foo();
  int b; std::cout << "b: " << &b << std::endl;
  {
    int c; std::cout << "c: " << &c << std::endl;
    foo();
  }
  int d; std::cout << "d: " << &d << std::endl;
}

This produces this output on my machine:

$ ./stack.exe
a: 0x28cd30
foo:e 0x28cd04
b: 0x28cd2c
c: 0x28cd24
foo:e 0x28cd04
d: 0x28cd28

Since the stack grows downward, we can see the order in which things are put onto the stack: a, b, d, and c in that order, and then the two calls to foo() put its e in the same place both times. This means that the same amount of memory has been allocated on the stack both times that foo() is called, even though several variable declarations (including one within an internal scope) intervene. Thus, in this case we can conclude that all the stack memory for the local variables in main() was allocated at the beginning of main() rather than being incremented incrementally.

You can also see that the compiler arranges things so that constructors are called in descending stack order, and destructors are called in ascending order -- everything is the bottom constructed thing on the stack when it's constructed and when it's destructed, but this does not mean that it's the bottom thing for which space has been allocated, or that there isn't currently-unused space above it on the stack for things that haven't been constructed yet (such as the space for d when c or the two incarnations of foo:e are constructed).

Brooks Moses
  • 8,739
  • 2
  • 28
  • 57
  • @Brooks Moses: I like your experimental setup. The output on one of my setup (Cygwin on i386, using g++ 3.4.4). is slightly different. I got the same address for both 'c' and 'd'. – Arun Mar 01 '10 at 06:10
  • Thanks. That's an interesting result -- it's a GCC 3.4 to 4.x difference, I would guess; I was also using Cygwin on i386, but was using the g++ 4.3.2 compiler. (A much-recommended upgrade, btw, but you have to explicitly select gcc4 in the Cygwin setup program to get it.) – Brooks Moses Mar 01 '10 at 21:52
2

This will be compiler dependent, but typically the variable int a will be allocated on the stack at the time the function is called.

Kyle Lutz
  • 7,592
  • 2
  • 18
  • 21
  • -1, all compiler has the same memory allocation semantic when dealing with local variables. they will be allocated upon declaration, and deallocated when goes out of scope like what Brian said. in case of multiple local variable, they are deallocated using LIFO paradigm, where the last allocated memory get freed first. – YeenFei Mar 01 '10 at 05:14
  • 3
    Wow, that's a pretty insane statement to make YeenFei. "all compiler has the same memory allocation semantic when dealing with local variables". Chances are that's true for *some* relatively low-level compilers, but not all. Many compilers will allocate storage for a variable the first time it is read or written to. Consider `int a; int b=2; a=3;`, the compiler might allocate memory for `a` after `b`. – Ponkadoodle Mar 01 '10 at 05:17
  • 2
    Also, note that this is a very simple form of allocation; in general all local variables within a scope are given compile-time offsets from a saved pointer to the stack, and so when the scope is entered, the program "allocates" those variables by saving the current stack pointer somewhere and then bumping up by the total size of the local variables. It's much more efficient to do that all at once than to do it incrementally, and it means that you can find all the local variables with the same varying pointer and some constants, rather than having to store different varying pointers for each. – Brooks Moses Mar 01 '10 at 05:18
  • There very well may be a C interpreter that allocates all variables on the heap, who knows. – Grant Paul Mar 01 '10 at 05:36
  • YeenFei: That's a clever idea! – Brooks Moses Mar 01 '10 at 05:43
  • Brooks : ur example cleared my confusion on allocation vs initialization. good work :) – YeenFei Mar 01 '10 at 06:18
2

At least as things are typically implemented, it's in between the two. When you call a function, the compiler will generate code for the function call that evaluates the parameters (if any) and puts them in registers or on the stack. Then, when execution reaches the entry for the function, space for local variables will be allocated on the stack, and locals that need initialization will be initialized. At that point, there might be some code to save registers that are used by the function, shuffle values around to get them into the desired registers and such. The code for the body of the function starts to execute after that.

Jerry Coffin
  • 437,173
  • 71
  • 570
  • 1,035