31

I am starting again with c++ and was thinking about the scope of variables. If I have a variable inside a function and then I return that variable will the variable not be "dead" when it's returned because the scope it was in has ended?

I have tried this with a function returning a string and it did work. Can anyone explain this? Or at least point me to some place that can explain this to me please.

Thanks

dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
AntonioCS
  • 8,335
  • 18
  • 63
  • 92

6 Answers6

54

When the function terminates, the following steps happen:

  • The function’s return value is copied into the placeholder that was put on the stack for this purpose.

  • Everything after the stack frame pointer is popped off. This destroys all local variables and arguments.

  • The return value is popped off the stack and is assigned as the value of the function. If the value of the function isn’t assigned to anything, no assignment takes place, and the value is lost.

  • The address of the next instruction to execute is popped off the stack, and the CPU resumes execution at that instruction.

The stack and the heap

Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • That's a nice article! I bookmarked it for sharing with people learning about the stack :) – Jason Coco Nov 08 '08 at 21:35
  • 3
    Standard C++ has no notion of a stack. – fredoverflow Dec 08 '11 at 21:30
  • 4
    @CMS I am curious if there is a section in the C++ standard which supports this statement? As far as I know this information about the stack is usually part of the calling convention of the specific CPU architecture and does not have to be the same for each available platform. – MKroehnert Jan 27 '12 at 16:18
5

It really depends on what kind of variable you are returning. If you return a primitive then it is returned by copy, not by reference so the value is copied to the top of the stack (or, more often placed into a register) where the calling function can get it. If you allocate an object or memory on the heap and return a pointer then it doesn't die because it's on the heap, not the stack. If you allocate something on the stack, however, and return it, that would be a bad thing. For instance, either of these would be very bad:

int *myBadAddingFunction(int a, int b)
{
    int result;

    result = a + b;
    return &result; // this is very bad and the result is undefined
}

char *myOtherBadFunction()
{
    char myString[256];

    strcpy(myString, "This is my string!");
    return myString; // also allocated on the stack, also bad
}
Jason Coco
  • 77,985
  • 20
  • 184
  • 180
  • I am actually returning a string.. it's not exactly an array but I am declaring it in the function like so: string funcnam(parms...) { string test; return string; } And it's doing ok – AntonioCS Nov 08 '08 at 21:48
  • If it's an actual string object it does fine because it's getting allocated on the heap, not on the stack. In the bad example above myString isn't actually an object, just a standard char array. CMS's Stack and Heap article is very good, you should give it a read :) – Jason Coco Nov 08 '08 at 21:52
4

When you return a value, a copy is made. The scope of the local variable ends, but a copy is made, and returned to the calling function. Example:

int funcB() {
  int j = 12;
  return j;
}

void A() {
  int i;
  i = funcB();
}

The value of j (12) is copied and returned to i so that i will receive the value of 12.

Kieveli
  • 10,944
  • 6
  • 56
  • 81
  • what if return value is an array? i need to malloc it before return? since array holds the first address of memory – TomSawyer Jun 07 '20 at 07:44
  • 1
    You would dynamically allocate an array (new / malloc). Your dynamic allocation will assign a pointer value to a local variable, and the value of that pointer would be returned. Returning an array that was locally declared (int[] var; std::array othervar;) would be a mistake - the scope would finish when the function exits, and your array would be invalid. – Kieveli Jun 09 '20 at 15:42
  • thank you, so if `i` in this case is a struct object, then i pass `&i` to it to `funcB` as an agrument - `funcB` doesn't return anything, just use `i` struct members to calculation , do i need to malloc `i`? No, right? since `i` value is still in the scope of `void A()`, so i won't be afraid of it to be ended of lifetime after `funcB` is finished – TomSawyer Jun 11 '20 at 07:04
  • Right - your solution is better than getting funcB() to malloc and return a pointer. It's easy to get memory leaks if you don't allocate and free memory in the same place. – Kieveli Jun 26 '20 at 14:30
4

Just for a little bit more of a memory-model oriented explanation: when a function is called, a temporary space is made for the function to put its local variables, called a frame. When the function (callee) returns its value, it puts the return value in the frame of the function that called it (caller), and then the callee frame is destroyed.

The "frame is destroyed" part is why you can't return pointers or references to local variables from functions. A pointer is effectively a memory location, so returning the memory location of a local variable (by definition: a variable within the frame) becomes incorrect once the frame is destroyed. Since the callee frame is destroyed as soon as it returns its value, any pointer or reference to a local variable is immediately incorrect.

cdleary
  • 69,512
  • 53
  • 163
  • 191
3

This depends on the type of the returned item. If you are returning by value, a new copy of the variable is made to return to the caller. I thins case you do not need to worry about object lifetime, but you may need to worry about the costs of copying objects (but please don't prematurely optimze - correctness is much more important):

std::string someFunc( std::string& const s)
{
    return s + "copy";
}

If the function is returning a reference, then you need to be careful about what you're returning because it's lifetime needs to extend beyond the function's lifetime and the caller will not necessarily be able todelete it if you're using new to create the object:

std::string& someFunc2( std::string const& s)
{
    return s + "reference to a copy";   // this is bad - the temp object created will 
                                        //  be destroyed after the expression the 
                                        //  function call is in finishes.
                                        //  Some, but not all, compilers will warn 
                                        //  about this.
}

Of course, returning pointers will have similar lifetime considerations.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
2

The local variable is copied to the return value. Copy constructors are called for non-trivial classes.

If you return a pointer or reference to a local variable you will have trouble---just as your intuition suggested.

dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234