11

Suppose this:

void func() { ... if( blah ) { int x; } ... } 

Is the space for x reserved on the stack immediately when func is entered, or only if the block is actually executed?
Or is it the compiler's choice?
Do C and C++ behave the same about this?

1
  • 3
    That's implementation defined, so you should look at a few examples of code generated by different compilers to have an idea. In this talk by Microsoft, the presenter talks briefly about what he calls "stack packing" on the VisualC++ compiler, which is probably an optimization to only allocate the minimum needed stack space for each function. Commented Dec 28, 2015 at 17:15

4 Answers 4

7

It is really the compiler's choice, (compiler implementation dependent,) so you cannot rely on any particular behavior.

All I can tell you is my observations.

When I compile without optimizations:

  • The compiler tends to do things in a rather clear cut, deterministic, and reliable fashion:

    • The compiler does not optimize away any local variables.

    • Stack space for all local variables, no matter how they are nested, is reserved at once when the function is entered.

    • Stack space is not reused across non-overlapping nested scopes. This means that void f(){ { int x; } { int y; } } causes space to be allocated for two int variables; the space allocated for x is not reused by y.

When I compile with optimizations:

  • All that Loki Astari wrote is true:

    • The compiler may completely eliminate any local variable.

      • For example, if you invoke a function, store the result in a local variable, then immediately pass that local variable to another function, and never use that local variable again, then the compiler will not only eliminate the unnecessary write and read to and from that local variable, it will also eliminate the allocation of stack space for it, too. It will be as if the variable was never declared.
    • The compiler may turn any local variable into a register variable.

      • Which usually results in no stack space being allocated for it, unless you take a reference to it at some point, in which case the compiler may temporarily store it so that it can be referenced. (Or the compiler may decide to not use a register at all for a variable whose address is taken, it is really up to the compiler.)
    • The remaining variables:

      • May share the same stack location if they are in non-overlapping scopes.

      • May share the same stack location even if they are in the same scope, or in overlapping scopes, if the compiler can prove, via flow analysis, that their lifetimes do not overlap.

As for difference in behavior between C and C++:

  • The language specifications do not mandate any particular behavior, so all bets are up.

  • In general, different compilers will behave differently.

  • However, if you are using a single compiler that is capable of compiling both C and C++ then there is a higher degree of likelihood that it will do things the same way in both languages, because at a low level, things are similar.

13

Who said the compiler will reserve any space (could be register only).

This is completely undefined.
All that you can say is that it (x) can only be accessed from inside the inner block.

How the compiler allocates memory (on a stack if it even exists) is completely upto the compiler (as the memory region may be re-used for multiple objects (if the compiler can prove that their lifespans do not overlap)).

Is the space for x reserved on the stack immediately when func is entered

Undetermined.

or only if the block is actually executed?

Undetermined.
But if x was a class object then the constructor will only be run if the block is entered.

Or is it the compiler's choice?

The compiler may not even allocate memory.

Do C and C++ behave the same about this?

Yes

2
  • 4
    I would posit that worrying too much about how the compiler handles this would be premature optimization for most applications. Commented Dec 18, 2011 at 20:21
  • @TehShrike That's why I posted this on engineering and not stackoverflow, it's not a trivial subject for engineers to think about. Commented Feb 14, 2022 at 15:17
2

The variable x only exists while the block is being executed. That's how it is defined in the language.

Now the compiler has to allocate memory while the block is being executed. Assume the compiler figures out "I need 16 bytes when the block is not being executed, and 24 bytes when it is executed". It can follow two strategies: Allocate 16 bytes, then allocate 8 more bytes when the block is entered, and release the 8 bytes when leaving the block. Or it can just allocate 24 bytes permanently. Because this is not something your code can observe, the compiler is free to chose either way. The compiler would then decide "I can either have two more instructions and execute them every time I enter the block, or have eight bytes allocated but unused most of the time". I think the unused eight bytes are better.

On the other hand, assume it was not "int x" but "int x [20000]". Now we have two instructions vs. 80,000 or 160,000 bytes. The compiler might decide the other way this time, because the numbers are different.

Now assume we have

for (int i = 0; i < 10000; ++i) { int x[20000]; ... } 

It would likely be best to allocate x not 10,000 times within the for loop, but just once just before the for loop, and deallocate just after the loop, because according to the language, x would be allocated except for the tiny amount of time just between the "++i" and the "i < 10000".

But it's completely up to the compiler.

-1

Let's assume to want such a function:

void A(bool b) { if (b) { char a[45]; do_something(a,45); }else{ char a[22]; do_otherthing(a,22); } char c[20]; do_some_common(c,20); } 

On a small 8-bit microcontroller with limited stack space, you may expect that this function will use up 45 byte because the three arrays may be aliased by optimizer. However, as stack space is not so valuable for medium or large processors, a typical compiler allocates 45+22+20 byte at once at entry. To be safe not to allocate so much stack space at once, put every function with its array in a helper function and disable inlining.

1
  • a typical compiler allocates 45+22+20 byte at once at entry Cite? Commented May 28 at 13:41

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.