149

Is the following code (func1()) correct if it has to return i? I remember reading somewhere that there is a problem when returning a reference to a local variable. How is it different from func2()?

int& func1()
{
    int i;
    i = 1;
    return i;
}

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}
3

3 Answers 3

232

This code snippet:

int& func1()
{
    int i;
    i = 1;
    return i;
}

will not work because you're returning an alias (a reference) to an object with a lifetime limited to the scope of the function call. That means once func1() returns, int i dies, making the reference returned from the function worthless because it now refers to an object that doesn't exist.

int main()
{
    int& p = func1();
    /* p is garbage */
}

The second version does work because the variable is allocated on the free store, which is not bound to the lifetime of the function call. However, you are responsible for deleteing the allocated int.

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}

int main()
{
    int* p = func2();
    /* pointee still exists */
    delete p; // get rid of it
}

Typically you would wrap the pointer in some RAII class and/or a factory function so you don't have to delete it yourself.

In either case, you can just return the value itself (although I realize the example you provided was probably contrived):

int func3()
{
    return 1;
}

int main()
{
    int v = func3();
    // do whatever you want with the returned value
}

Note that it's perfectly fine to return big objects the same way func3() returns primitive values because just about every compiler nowadays implements some form of return value optimization:

class big_object 
{ 
public:
    big_object(/* constructor arguments */);
    ~big_object();
    big_object(const big_object& rhs);
    big_object& operator=(const big_object& rhs);
    /* public methods */
private:
    /* data members */
};

big_object func4()
{
    return big_object(/* constructor arguments */);
}

int main()
{
     // no copy is actually made, if your compiler supports RVO
    big_object o = func4();    
}

Interestingly, binding a temporary to a const reference is perfectly legal C++.

int main()
{
    // This works! The returned temporary will last as long as the reference exists
    const big_object& o = func4();    
    // This does *not* work! It's not legal C++ because reference is not const.
    // big_object& o = func4();  
}
8
  • 2
    Beautiful explanation. :hattip: In the third code snippet, you are deleting int* p = func2(); delete p; Now, when you deleted 'p', does it mean that the memory allocated "inside" the function func2() 's definition also got deleted? Commented Aug 27, 2011 at 9:24
  • 2
    @Anisha Kaul: Yes. The memory was allocated inside func2() and released outside in the next line. It is a rather error-prone way to handle memory though, like I said you would use some variant of RAII instead. By the way, you sound like you're learning C++. I recommend picking up a good introductory C++ book to learn from. Also, for future reference if you have a question, you can always post the question on Stack Overflow. Comments are not meant for asking totally new questions.
    – In silico
    Commented Aug 27, 2011 at 9:34
  • Now I understood, you've done it right! The function was returning a pointer, and outside that function, you have deleted the memory to which it was pointing to. It is clear now, and thanks for the link. Commented Aug 27, 2011 at 9:38
  • and you have edited the answer?? :mad: I could have missed it easily. ;) ;) Commented Aug 27, 2011 at 10:05
  • @Anisha Kaul: No, I didn't. The last time I edited my answer was on January 10th, according to the time stamp under my post.
    – In silico
    Commented Aug 27, 2011 at 10:18
24

A local variable is memory on the stack, and that memory is not automatically invalidated when you go out of scope. From a function deeper nested (higher on the stack in memory), it’s perfectly safe to access this memory.

Once the function returns and ends though, things get dangerous. Usually the memory is not deleted or overwritten when you return, meaning the memory at that address is still containing your data - the pointer seems valid.

Until another function builds up the stack and overwrites it. This is why this can work for a while - and then suddenly cease to function after one particularly deeply nested set of functions, or a function with really huge sized or many local objects, reaches that stack-memory again.

It even can happen that you reach the same program part again, and overwrite your old local function variable with the new function variable. All this is very dangerous and should be heavily discouraged.

Do not use pointers to local objects!

2

A good thing to remember are these simple rules, and they apply to both parameters and return types...

  • Value - makes a copy of the item in question.
  • Pointer - refers to the address of the item in question.
  • Reference - is literally the item in question.

There is a time and place for each, so make sure you get to know them. Local variables, as you've shown here, are just that, limited to the time they are locally alive in the function scope. In your example having a return type of int* and returning &i would have been equally incorrect. You would be better off in that case doing this...

void func1(int& oValue)
{
    oValue = 1;
}

Doing so would directly change the value of your passed in parameter. Whereas this code...

void func1(int oValue)
{
    oValue = 1;
}

would not. It would just change the value of oValue local to the function call. The reason for this is because you'd actually be changing just a "local" copy of oValue, and not oValue itself.

Not the answer you're looking for? Browse other questions tagged or ask your own question.