3

Possible Duplicate:
Returning the address of local or temporary variable

The add function is implemented wrongly. It should return a value instead of a pointer. Why aren't any errors when ans and *ans_ptr are printed and the program even gives correct result? I guess the variable of z is already out of scope and there should be segmentation fault.

#include <stdio.h>

int * add(int x, int y) {
    int z = x + y;
    int *ans_ptr = &z;
    return ans_ptr;
}

int main() {
    int ans = *(add(1, 2));
    int *ans_ptr = add(1, 2);

    printf("%d\n", *ans_ptr);
    printf("%d\n", ans);

    return 0;
}
Community
  • 1
  • 1
Byron
  • 31
  • 2
  • 3
    Read the answer with the 1845 upvotes: http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope – Nordic Mainframe Sep 06 '11 at 14:52

5 Answers5

9

The reason it 'works' is because you got lucky. Returning a pointer to a local variable is Undefined Behaviour!! You should NOT do it.

int * add(int x, int y) {
    int z = x + y; //z is a local variable in this stack frame
    int *ans_ptr = &z; // ans_ptr points to z
    return ans_ptr;
}


// at return of function, z is destroyed, so what does ans_ptr point to? No one knows.  UB results
Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • Truth be told, this simple case is quite likely to "work". It'll break if anything manipulates the stack pointer between the call to `add` and the getting of the value, but the value isn't technically destroyed -- just forgotten about. It's still a horrible idea, though; once the function returns, it no longer owns that little piece of the stack, and any function call (or signal, or possibly interrupt, etc) can overwrite the value. It's UB according to the standard, but the behavior is pretty much the same in any architecture that passes values around on the stack. – cHao Sep 06 '11 at 14:49
  • I don't think it happens randomly. This is a question from my CS course. Try it on your machine and you can get the correct result. I want to know HOW this happens – Byron Sep 06 '11 at 14:50
  • 1
    @Bryon, look at the definition of 'Undefined Behaviour', it means **anything** can happen, it could overwrite the MBR if it wanted to. – Tony The Lion Sep 06 '11 at 14:55
  • @Tony: It could, but it won't. The C specs don't define what will happen, so it says "anything can happen". But every machine you test this on (assuming it uses the stack for local variables) will do one of three things: return the correct value, return a wrong value, or segfault. It won't set fire to anything -- or overwrite your MBR, or order a dozen pizzas, or anything else -- unless you *rely* on the value for something important. – cHao Sep 06 '11 at 15:02
  • 1
    @Tony: note also that if this code overwrites the MBR when run by a normal user then regardless of what the C standard has to say on the subject, that's an OS bug. Just because the C standard says your OS vendor can run in front of a bus, doesn't mean it will. I think cHao's point is just that although it's UB, and hence the C standard has not the faintest clue whether or why it worked, we're intelligent human beings who know a little bit about typical C implementations, so we can hazard a guess at describing what happened on the user's machine, on this occasion. That is, *how* he got lucky. – Steve Jessop Sep 06 '11 at 15:08
  • @Steve: Exactly. Unless the compiler was written by a sadist, the behavior is to some degree predictable. It's still a really bad idea to rely on said behavior *in your program*, but it can be useful to explain after the fact why something "worked" or didn't. – cHao Sep 06 '11 at 15:18
  • @Tony The question was raised by my professor in an operating system course. I think my professor creates this question on purpose and there should be a reason behind it. The program works in all C compilers and I don't think it is just luck. – Byron Sep 06 '11 at 15:21
3

Because C has no garbage collection, when the "z" variable goes out of scope, nothing happens to the actual memory. It is simply freed for another variable to overwrite if the compiler pleases.

Since no memory is allocated between calling "add" and printing, the value is still sitting in memory, and you can access it because you have its address. You "got lucky."

However, as Tony points out, you should NEVER do this. It will work some of the time, but as soon as your program gets more complex, you will start ending up with spurious values.

Chriszuma
  • 4,464
  • 22
  • 19
0

No. Your question displays a fundamental lack of understanding of how the C memory model works.

The value z is allocated at an address on the stack, in the frame which is created when control enters add(). ans_ptr is then set to this memory address and returned.

The space on the stack will be overwritten by the next function that is called, but remember that C never performs memory clean up unless explicitly told to (eg via a function like calloc()).

This means that the value in the memory location &z (from the just-vacated stack frame) is still intact in the immediately following statement, ie. the printf() statement in main().

You should never ever rely on this behaviour - as soon as you add additional code into the above it will likely break.

kittylyst
  • 5,640
  • 2
  • 23
  • 36
0

The answer is: this program works because you are fortunate, but it will take no time to betray, as the address you return is not reserved to you anymore and any one can use it again. Its like renting the room, making a duplicate key, releasing the room, and after you have released the room at some later time you try to enter it with a duplicate key. In this case if the room is empty and not rented to someone else then you are fortunate, otherwise it can land you in police custody (something bad), and if the lock of the room was changed you get a segfault, so you can't just trust on the duplicate key which you made without acquisition of the room.

The z is a local variable allocated in stack and its scope is as long as the particular call to the function block. You return the address of such a local variable. Once you return from the function, all the addresses local to the block (allocated in the function call stack frame) might be used for another call and be overwritten, therefore you might or might not get what you expect. Which is undefined behavior, and thus such operation is incorrect.

If you are getting correct output, then you are fortunate that the old value held by that memory location is not overwritten, but your program has access to the page in which the address lies, therefore you do not get a segmentation fault error.

phoxis
  • 60,131
  • 14
  • 81
  • 117
0

A quick test shows, as the OP points out, that neither GCC 4.3 nor MSVC 10 provide any warnings. But the Clang Static Analyzer does:

ccc-analyzer -c foo.c
...
ANALYZE: foo.c add
foo.c:6:5: warning: Address of stack memory associated with local 
                    variable 'z' returned to caller
    return ans_ptr;
    ^      ~~~~~~~
Joseph Quinsey
  • 9,553
  • 10
  • 54
  • 77