tags:

views:

362

answers:

8

I have heard about the following scenario right when I started programming in C.

"Trying to access from outside, a functions local variable will result in error (or garbage value). Since the stack gets cleared off when we return from the function"

But my below code sample prints a value of 50. I am compiling the code with latest GCC compiler.

#include <stdio.h>

int * left();

int main()
{
      int *p=left();
      printf("%d\n",*p);
      return 0;
}

int * left()
{
        int i=50;
        return &i;
}

Enlight me on this issue.

Can I know the behaviour in C++ ?? Is it similar to c ..

+6  A: 

The behavior according to the C standard is undefined not garbage values. So it's possible, and occasionally likely, that the value will remain unchanged. This is not guaranteed by any means.

This is working by luck / chance / accident and nothing else. Don't rely on this behavior because it will come back to bite you.

JaredPar
Can I know the behaviour in C++ ??
codingfreak
The behaviour for the code you posted in C++ is the same: undefined behaviour.
Thanatos
You mean even in C++ we should avoid using the way shown above ?? But I have came across same kind of program in C++ primer plus in chapter 8 where the author is dynamically allocating memory in the user-defined function and freeing it on returning it to main function.
codingfreak
@codingfreak The C++ example you saw was very different from returning the adress of a local variable. The local variable's storage is automatically freed when the function returns. You saw an example where the storage wasn't freed automatically -- and thus needed to be freed explicitly by the programmer.
Pascal Cuoq
@Pascal - I agree with you. But dont you think it can send wrong message to new programmers that we can also do in the way given in example ??
codingfreak
+2  A: 

It works by accident. Memory location pointed by p still contains integer value 50 when printf() is called. But call to any function inside main() between left() and printf() would overwrite it.

For example, I don't know what would happen in your implementation if you changed your printf() call to:

printf("abs(%d) = %d ???\n", *p, abs(*p));

Don't do it!

Tomek Szpakowicz
Yeah calling another function between is changing the value
codingfreak
See my response for the reason.
Andy
+1  A: 

Undefined behavior. i is destroyed upon leaving left(). You have an address to garbage in p. The compiler hasn't destroyed i by luck, yet.

Alex
+3  A: 

Look at this modified example, where it shows clearly if something goes in between:

int *p=left();
// right here
printf("%d\n",*p);

You get a corrupted stack easily. The idea is that you don't own that place, someone else could use it!

int main()
{
    int *p=left();
    right(); // Look here! 
    printf("%d\n",*p); // prints -50 instead of 50
    return 0;
}
...
int * right()
{
    int i=-50;
    return &i;
}
AraK
The variable named "i" in right() is not the same one as in left(); it just happens to have the same name and use the same memory. Note however that p is not assigned to the address of right()'s i; it still has the address of left()'s i.
Nathan Kitchen
Yeah you are right ... it is getting changed.
codingfreak
@Nathan I am just trying to show how a stack can get corrupted easily if something goes in between, whether we use `right` or any code that uses the stack in the same thread :)
AraK
+2  A: 

You are referring to an object beyond its lifetime (the scope of left()). That results in undefined behaviour - I'd guess that you still get 50 because nothing has overwritten the area where i was yet.

Michael Foukarakis
Yeah it might be the case ....
codingfreak
+6  A: 

Modify it to add a second call to printf and you'll see a different value from the first time. Compile it with optimizations turned on and you'll see another set of values. Do anything with the value and you're stepping into undefined territory, which means that the compiler is free to summon demons through your nasal passages.

On my system, I see 50 and then 0; with optimizations I see 0 and then 32767.

If you make the local variable static, then you can return its address since it becomes just like a global (but remember that there is only one instance of it).

When a function returns, the local storage it was using on the stack is now considered "unused" by the program, since the stack doesn't go that high anymore. Typically, though, the values are still there, since there's no urgent need to clear them. The memory is also still owned by the program, since there's no sense in returning memory to the operating system a few bytes at a time. So for your specific example, under the circumstances in which you compiled it, the memory pointed to still contains the value 50. Officially, though, the value of *p is indeterminate, and attempts to use it result in undefined behavior.

One existential crisis of the C language is how on the one hand, it says nothing about the stack and the various bits of hexadecimal sludge that make up a running process; on the other hand, it's necessary to understand those in order to protect yourself from crashes, buffer overflows, and undefined behavior. Just remember that you're lucky that GCC gives a warning for this.

jleedev
offcourse adding optimizations might change the output value as the compiler trys to optimize the code
codingfreak
"on the other hand, it's necessary to understand those". It certainly helps to understand them, since then it's easier to remember what you can and can't do. But it's not strictly necessary provided you obey the standard. In this case, don't reference an object whose lifetime has expired (the lifetime of automatic variables ends when their block is exited). You don't need to know that this is implemented using a stack. The one thing you generally do need to avoid, in a platform-specific way, is running out of stack, i.e. don't recurse too deep.
Steve Jessop
+12  A: 

The variable 'i' is created on the stack and when the function 'left' returns, the stack is cleared. When I say cleared, it means the address used by variable 'i' is marked free for reuse. Till some other portion of code uses the same address, the value will remain unmodified. In your case, it is plain luck that gives you desired results. If you call few more functions after call to 'left', I am fairly certain you will get wrong results.

hackworks
Thats true .. i am getting different behaviour when I call the function in between
codingfreak
@codingfreak: also try it with the `-O2` optimization. On my test with GCC 3.4.5 I had `2009147472` output - GCC likely simply optimized away the store of `50` into the variable since the variable was never otherwise used during its lifetime.
Michael Burr
Yeah adding optimizations does change the value ......
codingfreak
Can I know the behaviour in C++ ??
codingfreak
C++ works exactly the same as C in this case. *p value would be undefined.
Gorpik
+2  A: 

As far as the C standard is concerned, the value is undefined.

Practically, as long as nothing is pushed to the stack before the returned value is referenced, it works, but the next time a function is called the return address and new stack frame are pushed to the stack and may overwrite the referenced value.

This would not be considered acceptable coding (because of the undefined nature).

Andy
It's not just the _value_ that's undefined, it's the _behavior_ that's undefined. You can get an access violation there. You can overwrite some system structure and crash the whole OS (think DOS and the likes). In fact, you can make your monitor blow up in your face. ~
Pavel Minaev
"If you have a sound card it swears at you" - http://www.dilbert.com/strips/comic/1995-09-17/
Steve Jessop
I agree that it's both the value and the behavior.
Andy