Region
Variables declared inside the scope of the { } pair are on the stack. This applies to variables declared at the beginning of a function or any pair { } inside a function.
int myfunc() { int i = 0; // On the stack, scoped: myfunc printf("%i\n"); if (1) { int j = 1; // On the stack, scope: this if statement printf("%i %i\n",i,j); } printf("%i %i\n",i,j); // Won't work, no j }
Currently, the scope of variables is limited to the surrounding { } . I remember that some old Microsoft compilers did not limit the scope, and that in the example above, the final printf() will compile.
So where is it in memory?
Memory i and j simply reserved on the stack. This is not the same as allocating memory with malloc() . This is important because the call to malloc() very slow in comparison. Also with dynamic memory allocation using malloc() you should call free() .
In fact, the compiler knows in advance what space is needed for the function variables, and will generate code that refers to the memory as to what the stack pointer is when calling myfunc() . As long as the stack is large enough (usually 2 MB, OS dependent), all is well.
A stack overflow occurs in a situation where myfunc() is called with a stack pointer already close to the end of the stack (i.e. myfunc() is called by a function that in turn was called by another, which it itself called another, etc. Each layer of nested function calls moves the stack pointer a bit more and returns only when functions return).
If the space between the stack pointer and the end of the stack is not large enough to hold all the variables declared in myfunc() , the code for myfunc() will simply try to use locations outside the stack. This is almost always bad, and exactly how bad and how hard it is to notice that something went wrong depends on the operating system. On small embedded microcontrollers, this can be a nightmare, as this usually means that some other part of the program data (for example, global variables) is silently overwritten, and it can be very difficult to debug. On large systems (Linux, Windows), the OS will tell you what happened, or just make the stack bigger.
performance considerations
In the above example, I assign the values i and j . It actually takes a small amount of runtime. j assigned 1 only after evaluating the if statement and the subsequent branch, where j declared.
Say, for example, the if statement was not evaluated as true; in this case, j never assigned 1. If j was declared at the beginning of myfunc() , then it was always assigned the value 1 regardless of whether the if statement was true - a small waste of time. But consider a less trivial example when a large array is declared initialized; which will take longer to complete.
int myfunc() { int i = 0; // On the stack, scoped: myfunc int k[10000] = {0} // On the stack, scoped: myfunc. A complete waste of time // when the if statement evaluates to false. printf("%i\n"); if (0) { int j = 1; // On the stack, scope: this if statement // It would be better to move the declaration of k to here // so that it is initialised only when the if evaluates to true. printf("%i %i %i\n",i,j,k[500]); } printf("%i %i\n",i,j); // Won't work, no j }
Placing the declaration of k at the top of myfunc() means that a 10000 long loop is executed to initialize k every time myfunc() called. However, it is never used, so a cycle is a waste of time.
Of course, in these trivial examples, compilers optimize unnecessary code, etc. In real code, where the compiler cannot predict in advance what the execution flow will be, everything remains in place.