15

Given the following function, will each of the local variables be declared on the stack?

std::string reallyCoolFunction(unsigned int a)
{
   if( a < 20 ) 
   {
     std::string result1 = "This function is really cool";
     return result1;
   }

   if( a >=20 && a <= 40 )
   {
     std::string result2 = "This function is kind of cool";
     return result2;
   }

   if( a > 40 )
   {
     std::string result3 = "This function is moderately cool";
     return result3;
   }

 std::string result4 = "This function really isn't that cool"; 
 return result4; // remove warning

}

In this situation, only one std::string is actually required, do all 4 get allocated on the stack, or does only 1 get allocated?

lcs
  • 4,227
  • 17
  • 36

6 Answers6

14

The decision is up to the compiler: since the automatic variables go out of scope before the next one comes in scope, the compiler can re-use their memory. Keep in mind that "stack" variables are actually variables with automatic storage duration according to the C++ specification, so they may not be on the stack at all.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • I believe there is an important bit in my answer that you forget to mention - the fact that most of the string will not be allocated on the stack. I think it is worth mentioning – Ivaylo Strandjev Jan 22 '13 at 15:19
  • @IvayloStrandjev Absolutely - I upvoted your answer for that very reason :) – Sergey Kalinichenko Jan 22 '13 at 15:20
  • automatic storage has guaranteed stack behavior. that's what "the stack" refers to. "actually" is actually a pretty meningless word. an automatic variable can only avoid being allocated on the stack by being optimized away under the as-if rule, and then it's *as if* it was allocated on the stack, hence not very meaningful to make a big note about that. – Cheers and hth. - Alf Jan 22 '13 at 15:55
10

On most compilers only one string will be allocated. Keep in mind though std::string uses dynamic memory so most of its content will still be allocated on the heap.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
  • 5
    The string *may* use dynamic memory but does not necessarily. See for example the small string optimization in MSVC' standard library. – Arne Mertz Jan 22 '13 at 15:27
6

Most probably 0 or maybe 1 (in Release) and certainly 4 (in Debug).

This is called the RVO: Return Value Optimization.

The compiler is actually allowed to elide the copy entirely and build the std::string directly in the slot provided by the caller. This is ABI specific, and as all optimizations only applies if a number of criteria are met; in your case it's probable that it will apply.

If you want to check, you can try to peruse the output of the compiler at various stages of its translation/optimization pipeline; it might be hard though depending on your toolchain.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
2

Depends on the compiler.
If the compiler is intelligent enough to determine conclusively that only one string is needed it will emit code only for one string.

Is your compiler intelligent enough?

Easiest way is to check the the generated assembly code.

Do all 4 get allocated on the stack, or does only 1 get allocated?

Be it 1 or 4 strings, the string object is located on the stack local to the function but the memory for the string is allocated in freestore.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • *Be it 1 or 4 strings, the string object is located on the stack local to the function but the memory for the string is allocated in freestore.* => SSO: Short String Optimization (implemented in Dirkumware and libc++) – Matthieu M. Jan 22 '13 at 15:43
  • @MatthieuM.: True & agree. Thank you for the correction. Just to be more precise this is an implementation detail.The standard does not mandate *where* the storage has to be done it only mandates certain observable behaviors that the implementation of `std::string` has to comply with. – Alok Save Jan 22 '13 at 15:49
  • 1
    Yes, one notable change between C++03 and C++11 though is that gcc's optimization (Copy On Write) is not possible any longer due to new constraints. – Matthieu M. Jan 22 '13 at 16:01
  • @MatthieuM.: That is interesting. Can you please(*if possible*) provide me some references to explore a bit more in this regards. – Alok Save Jan 22 '13 at 16:08
  • See http://stackoverflow.com/questions/12199710/legality-of-cow-stdstring-implementation-in-c11 – Matthieu M. Jan 22 '13 at 16:10
0

The compiler is allowed to create 4, 1, 2 or 3 variables in this case. But most compilers that I'm aware of would create only one, or perhaps two, since the result4 is within the entire scope of the function.

Of course, if you do the "right" things the compiler may well get confused and do more than it absolutely needs to, so relying on this in critical functionality wouldn't be a particularly good thing.

Edit: I should add that the constructor for the std::string should only be run if the object is actually "used", so you may get the stack-space used, but it should not call the constructor. This is important if you do something like this:

void func()
{
    if (something)
    {
        Lock myLock(&global_lock_object);   // Constructor locks global_lock_object
        ... do stuff that needs global_lock_object locked ... 
        // end of scope runs destructor of Lock that unlocks global_lock_object. 
    }
    ... more code that takes a long time to execute but doesn't need lock. ...
}

Now, if the constructor for Lock was executed "too early", and destructed in the same scope [and it should be symmetrical], the lock would be held for the entire duration of the function, which would be wrong.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
0

Short answer: look at the assembler.

Long answer: The compiler may apply static checks to determine if he needs all 4 or just some of the variables. Some compilers might allocate 4 different variables in debug mode, some might not. In Release mode, some optimizers might see that the first 3 are each in their own scope and thus can be put in the same place. Those compilers could therefore reserve space for two string variables on the stack. It needs just a bit more analysis to see that the fourth variable in no case coexists with the first three, so some optimizers might put the remainig two variables in the same place as well.

But whether your compiler does that, and whether he still does that in a just slightly more complicated situation, can only be determined for sure if you analyze the ouput.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90