I have the following code.

int * foo()
{
    int a = 5;
    return &a;
}

int main()
{
    int* p = foo();
    cout << *p;
    *p = 8;
    cout << *p;
}

And the code is just running with no runtime exceptions!

The output was 58

How can it be? Isn't the memory of a local variable inaccessible outside its function?

11 upvote
  flag
this won't even compile as is; if you fix the nonforming business, gcc will still warn address of local variable ‘a’ returned; valgrind shows Invalid write of size 4 [...] Address 0xbefd7114 is just below the stack ptr – sehe
3 upvote
  flag
In some platforms/compilers (especially old compilers for DOS) you can even write through NULL pointer and everything seems OK until you overwrite something important (like the code being executed). :) – Serge Dundich
1 upvote
  flag
@Serge that's because most OS-es these days have a write-protected zero-page however not all of them do! – Jasper Bekkers
59 upvote
  flag
@Serge: Back in my youth I once worked on some kinda tricky zero-ring code that ran on the Netware operating system that involved cleverly moving around the stack pointer in a way not exactly sanctioned by the operating system. I'd know when I'd made a mistake because often the stack would end up overlapping the screen memory and I could just watch the bytes get written right onto the display. You can't get away with that sort of thing these days. – Eric Lippert
1 upvote
  flag
Ah man this makes me miss my C++ / DCOM / VB days. We had a home-grown red-black tree that had invalid pointer access issues. I had the distinct pleasure of debugging it. – xanadont
upvote
  flag
@Jasper Bekkers: "that's because most OS-es these days have a write-protected zero-page however not all of them do!" Yea. I know. – Serge Dundich
3 upvote
  flag
@Xeo - Not everyone is super wizards in C++ as you probably. Some of us still have a few things to learn, especially when comming to C++ from "safer" languages like C# and java. So unless you are really intented to teach me something here, please save me your comments. – Avraham Shukron
upvote
  flag
@Eric Lippert: reminds me of this: j00ru.vexillium.org/?p=781 – Yuhong Bao
4 upvote
  flag
@Xeo - I think you misunderstood me... I know it is unsafe, thats for sure! I thought it would be impossible. I guess i should get used to the freedom that C++ gives the developer.. – Avraham Shukron
19 upvote
  flag
lol. I needed to read the question and some answers before I even understood where the problem is. Is that actually a question about variable's access scope? You don't even use 'a' outside your function. And that is all there is to it. Throwing around some memory references is a totally different topic from variable scope. – erikbwork
upvote
  flag
@erickb - I guess you are right. now that i know the answer i also know that the title is not accurate. – Avraham Shukron
3 upvote
  flag
@Xeo: Joel tweeting this might have played a role in this being upvoted so much. – sbi
35 upvote
  flag
@sbi: Ya think? It makes me sad that a low-quality duplicate question can have so many upvotes just because it's got a great answer on it. A weakness of SO: votes are at least partially a function of views. – Lightness Races in Orbit
2 upvote
  flag
@Tomalak please provide a dupe link and I'm happy to vote for close. We can ask a moderator to merge with the question that this one is a dupe of. – Johannes Schaub - litb
7 upvote
  flag
Dupe answer doesn't mean dupe question. A lot of the dupe questions that people proposed here are completely different questions that happen to refer to the same underlying symptom... but the questioner has know way of knowing that so they should remain open. I closed an older dupe and merged it into this question which should stay open because it has a very good answer. – Joel Spolsky
14 upvote
  flag
@Joel: If the answer here is good, it should be merged into older questions, of which this is a dupe, not the other way around. And this question is indeed a dupe of the other questions proposed here and then some (even though some of the proposed are a better fit than others). Note that I think Eric's answer is good. (In fact, I flagged this question for merging the answers into one of the older questions in order to salvage the older questions.) – sbi
5 upvote
  flag
@Joel dupe means (quote) "This question covers exactly the same ground as earlier questions on this topic;", not "This question covers exactly the same ground as a newer question on this topic;". Either your merge or the "close" popup has it backwards. – Johannes Schaub - litb
1 upvote
  flag
But this way people don't manually have to click the forward link... so it may have been a good idea. But still the merge was backwards. Trying to justify by saying it was the right way around won't work. – Johannes Schaub - litb
2 upvote
  flag
Weird question w/ so much love, and I used to think that C developers MUST understand the how hardware works, the stack allocation has been the same forever. – bestsss
upvote
  flag
@Maxpm, zero page on 8086 (and 0000:0000 too) has its usages - interrupt vectors, etc, so addressing it was quite normal. Back in the day viruses (and anti-viruses) used to overwrite quite a bit of. – bestsss
1 upvote
  flag
@Avrahamshuk, there is nothing impossible, besides traveling faster than speed of light (and it's not absolutely certain) :) – bestsss
upvote
  flag
So memory is overwritten. Otherwise you would get '55' – Martin York
upvote
  flag
i mean it is not overwritten after i exit a function foo. And i can output it even if the local variable were destroyed. – Stals
upvote
  flag
this line was added after, i just forgot to write "EDIT" and there is a result -2 on a question. When i started this question i didnt know that this question was asked already. But when i found it i edited question with that i found a dublicate and instantly repotred my question. So i dont undertand why would you minus me. – Stals
upvote
  flag
can it cause segmentation fault ? – Debashish
upvote
  flag
@EricLippert, Re "you can't get away with that sort of thing these days", Why not? – Pacerier
upvote
  flag
@BoundaryImposition, So the great answer should somehow stand without a question? Or do you mean it should have instead be written on another domain.com? – Pacerier

20 Answers 11

Because the storage space wasn't stomped on just yet. Don't count on that behavior.

19 upvote
  flag
I like Eric's answer a lot, obviously, and I'm not just being egregious for the sake of it, but this answer has the virtue of brevity and correctness. – Rob Kent
1 upvote
  flag
Man, that was the longest wait for a comment since, "What is truth? said jesting Pilate." Maybe it was a Gideon's Bible in that hotel drawer. And what happened to them, anyway? Notice they are no longer present, in London at least. I guess that under the Equalities legislation, you would need a library of religious tracts. – Rob Kent
upvote
  flag
I could have sworn that I wrote that long ago, but it popped up recently and found my response wasn't there. Now I have to go figure out your allusions above as I expect I'll be amused when I do >.< – msw
1 upvote
  flag
Haha. Francis Bacon, one of Britain's greatest essayists, whom some people suspect wrote Shakespeare's plays, because they can't accept that a grammar school kid from the country, son of a glover, could be a genius. Such is the English class system. Jesus said, 'I am the Truth'. oregonstate.edu/instruct/phl302/texts/bacon/bacon_essays.htm‌​l – Rob Kent
upvote
  flag
And it's gone again. @RobKent, I just don't know how to make the comment "I like Eric's answer a lot too, that's why I upvoted it." stick. Odd. – msw
2 upvote
  flag
Very strange. Must be the secret 'Eric Lippert Modesty' feature in Stack Overflow. – Rob Kent
upvote
  flag
...and that makes it a "Great Question"! ;) i love one liners. – Sharky

You are just returning a memory address, it's allowed but probably an error.

Yes if you try to dereference that memory address you will have undefined behavior.

int * ref () {

 int tmp = 100;
 return &tmp;
}

int main () {

 int * a = ref();
 //Up until this point there is defined results
 //You can even print the address returned
 // but yes probably a bug

 cout << *a << endl;//Undefined results
}
upvote
  flag
I disagree: There is a problem before the cout. *a points to unallocated (freed) memory. Even if you don't derefence it, it is still dangerous (and likely bogus). – ereOn
upvote
  flag
@ereOn: I clarified more what I meant by problem, but no it is not dangerous in terms of valid c++ code. But it is dangerous in terms of likely the user made a mistake and will do something bad. Maybe for example you are trying to see how the stack grows, and you only care about the address value and will never dereference it. – Brian R. Bondy

Did you compile you program with the optimiser enabled ?

The foo() function is quite simple and might have been inlined/replaced in the resulting code.

But I aggree with Mark B that the resulting behavior is undefined.

upvote
  flag
That's my bet. Optimizer dumped the function call. – Erik Aronesty
9 upvote
  flag
That is not necessary. Since no new function is called after foo(), the functions local stack frame is simply not yet overwritten. Add another function invocation after foo(), and the 5 will be changed... – Tomas
upvote
  flag
I ran the program with GCC 4.8, replacing cout with printf (and including stdio). Rightfully warns "warning: address of local variable ‘a’ returned [-Wreturn-local-addr]". Outputs 58 with no optimization and 08 with -O3. Strangely P does have an address, even though its value is 0. I expected NULL (0) as address. – kevinf

In C++, you can access any address, but it doesn't mean you should. The address you are accessing is no longer valid. It works because nothing else scrambled the memory after foo returned, but it could crash under many circumstances. Try analyzing your program with Valgrind, or even just compiling it optimized, and see...

1 upvote
  flag
You probably mean you can attempt to access any address. Because most of the operating systems today will not let any program access any address; there are tons of safeguards to protect the address space. This is why there will not be another LOADLIN.EXE out there. – v010dya

You never throw a C++ exception by accessing invalid memory. You are just giving an example of the general idea of referencing an arbitrary memory location. I could do the same like this:

unsigned int q = 123456;

*(double*)(q) = 1.2;

Here I am simply treating 123456 as the address of a double and write to it. Any number of things could happen:

  1. q might in fact genuinely be a valid address of a double, e.g. double p; q = &p;.
  2. q might point somewhere inside allocated memory and I just overwrite 8 bytes in there.
  3. q points outside allocated memory and the operating system's memory manager sends a segmentation fault signal to my program, causing the runtime to terminate it.
  4. You win the lottery.

The way you set it up it is a bit more reasonable that the returned address points into a valid area of memory, as it will probably just be a little further down the stack, but it is still an invalid location that you cannot access in a deterministic fashion.

Nobody will automatically check the semantic validity of memory addresses like that for you during normal program execution. However, a memory debugger such as valgrind will happily do this, so you should run your program through it and witness the errors.

3 upvote
  flag
I'm just going to write a program now that keeps on running this program so that 4) I win the lottery – Aidiakapi

In typical compiler implementations, you can think of the code as "print out the value of the memory block with adress that used to be occupied by a". Also, if you add a new function invocation to a function that constains a local int it's a good chance that the value of a (or the memory address that a used to point to) changes. This happens because the stack will be overwritten with a new frame containing different data.

However, this is undefined behaviour and you should not rely on it to work!

3 upvote
  flag
"print out the value of the memory block with address that used to be occupied by a" isn't quite right. This makes it sound like his code has some well-defined meaning, which is not the case. You are right that this is probably how most compilers would implement it, though. – Brennan Vincent
up vote 4477 down vote accepted

How can it be? Isn't the memory of a local variable inaccessible outside its function?

You rent a hotel room. You put a book in the top drawer of the bedside table and go to sleep. You check out the next morning, but "forget" to give back your key. You steal the key!

A week later, you return to the hotel, do not check in, sneak into your old room with your stolen key, and look in the drawer. Your book is still there. Astonishing!

How can that be? Aren't the contents of a hotel room drawer inaccessible if you haven't rented the room?

Well, obviously that scenario can happen in the real world no problem. There is no mysterious force that causes your book to disappear when you are no longer authorized to be in the room. Nor is there a mysterious force that prevents you from entering a room with a stolen key.

The hotel management is not required to remove your book. You didn't make a contract with them that said that if you leave stuff behind, they'll shred it for you. If you illegally re-enter your room with a stolen key to get it back, the hotel security staff is not required to catch you sneaking in. You didn't make a contract with them that said "if I try to sneak back into my room later, you are required to stop me." Rather, you signed a contract with them that said "I promise not to sneak back into my room later", a contract which you broke.

In this situation anything can happen. The book can be there -- you got lucky. Someone else's book can be there and yours could be in the hotel's furnace. Someone could be there right when you come in, tearing your book to pieces. The hotel could have removed the table and book entirely and replaced it with a wardrobe. The entire hotel could be just about to be torn down and replaced with a football stadium, and you are going to die in an explosion while you are sneaking around.

You don't know what is going to happen; when you checked out of the hotel and stole a key to illegally use later, you gave up the right to live in a predictable, safe world because you chose to break the rules of the system.

C++ is not a safe language. It will cheerfully allow you to break the rules of the system. If you try to do something illegal and foolish like going back into a room you're not authorized to be in and rummaging through a desk that might not even be there anymore, C++ is not going to stop you. Safer languages than C++ solve this problem by restricting your power -- by having much stricter control over keys, for example.

UPDATE

Holy goodness, this answer is getting a lot of attention. (I'm not sure why -- I considered it to be just a "fun" little analogy, but whatever.)

I thought it might be germane to update this a bit with a few more technical thoughts.

Compilers are in the business of generating code which manages the storage of the data manipulated by that program. There are lots of different ways of generating code to manage memory, but over time two basic techniques have become entrenched.

The first is to have some sort of "long lived" storage area where the "lifetime" of each byte in the storage -- that is, the period of time when it is validly associated with some program variable -- cannot be easily predicted ahead of time. The compiler generates calls into a "heap manager" that knows how to dynamically allocate storage when it is needed and reclaim it when it is no longer needed.

The second is to have some sort of "short lived" storage area where the lifetime of each byte in the storage is well known, and, in particular, lifetimes of storages follow a "nesting" pattern. That is, the allocation of the longest-lived of the short-lived variables strictly overlaps the allocations of shorter-lived variables that come after it.

Local variables follow the latter pattern; when a method is entered, its local variables come alive. When that method calls another method, the new method's local variables come alive. They'll be dead before the first method's local variables are dead. The relative order of the beginnings and endings of lifetimes of storages associated with local variables can be worked out ahead of time.

For this reason, local variables are usually generated as storage on a "stack" data structure, because a stack has the property that the first thing pushed on it is going to be the last thing popped off.

It's like the hotel decides to only rent out rooms sequentially, and you can't check out until everyone with a room number higher than you has checked out.

So let's think about the stack. In many operating systems you get one stack per thread and the stack is allocated to be a certain fixed size. When you call a method, stuff is pushed onto the stack. If you then pass a pointer to the stack back out of your method, as the original poster does here, that's just a pointer to the middle of some entirely valid million-byte memory block. In our analogy, you check out of the hotel; when you do, you just checked out of the highest-numbered occupied room. If no one else checks in after you, and you go back to your room illegally, all your stuff is guaranteed to still be there in this particular hotel.

We use stacks for temporary stores because they are really cheap and easy. An implementation of C++ is not required to use a stack for storage of locals; it could use the heap. It doesn't, because that would make the program slower.

An implementation of C++ is not required to leave the garbage you left on the stack untouched so that you can come back for it later illegally; it is perfectly legal for the compiler to generate code that turns back to zero everything in the "room" that you just vacated. It doesn't because again, that would be expensive.

An implementation of C++ is not required to ensure that when the stack logically shrinks, the addresses that used to be valid are still mapped into memory. The implementation is allowed to tell the operating system "we're done using this page of stack now. Until I say otherwise, issue an exception that destroys the process if anyone touches the previously-valid stack page". Again, implementations do not actually do that because it is slow and unnecessary.

Instead, implementations let you make mistakes and get away with it. Most of the time. Until one day something truly awful goes wrong and the process explodes.

This is problematic. There are a lot of rules and it is very easy to break them accidentally. I certainly have many times. And worse, the problem often only surfaces when memory is detected to be corrupt billions of nanoseconds after the corruption happened, when it is very hard to figure out who messed it up.

More memory-safe languages solve this problem by restricting your power. In "normal" C# there simply is no way to take the address of a local and return it or store it for later. You can take the address of a local, but the language is cleverly designed so that it is impossible to use it after the lifetime of the local ends. In order to take the address of a local and pass it back, you have to put the compiler in a special "unsafe" mode, and put the word "unsafe" in your program, to call attention to the fact that you are probably doing something dangerous that could be breaking the rules.

For further reading:

6 upvote
  flag
If the hotel were about to be replaced by a football stadium, wouldn't you notice the lack of people? Or the monstrous army of giant bulldozers outside? – Mateen Ulhaq
48 upvote
  flag
@muntoo: Unfortunately it's not like the operating system sounds a warning siren before it decommits or deallocates a page of virtual memory. If you're mucking around with that memory when you don't own it anymore the operating system is perfectly within its rights to take down the entire process when you touch a deallocated page. Boom! – Eric Lippert
5 upvote
  flag
I like the analogy, but nearly all hotels use programmable key cards that get locked out at a specified time, or when a new key is issued for that room, whichever comes first. And I would imagine the very few hotels that do not use such a system would be very insistent that you return your key at checkout. – Kyle Cronin
11 upvote
  flag
That's a great analogy, but bashing C++ at the end is not OK. C++ doesn't impose too many restrictions, but that lack of restrictions normally pays back in measurable performance gains. – cyberguijarro
69 upvote
  flag
@Kyle: Only safe hotels do that. The unsafe hotels get measurable profit gains from not having to waste time on programming keys. – Alexander Torstling
25 upvote
  flag
@cyberguijarro I don't think he is bashing C++ at the end. C++ is not safe, and as you say, this is a good thing in a lot of situations. Likewise safer languages are less powerful, but can be easier to use. They're just different. – Edd
454 upvote
  flag
@cyberguijarro: That C++ is not memory safe is simply a fact. It's not "bashing" anything. Had I said, for example, "C++ is a horrid mishmash of under-specified, overly-complex features piled on top of a brittle, dangerous memory model and I am thankful every day I no longer work in it for my own sanity", that would be bashing C++. Pointing out that it's not memory safe is explaining why the original poster is seeing this issue; it's answering the question, not editorializing. – Eric Lippert
27 upvote
  flag
@Eric: C# (really, .NET) isn't "safe" either in that respect. I can combine Math.Random, IntPtr, and Marshal.Copy and cause total chaos (no unsafe keyword nor /unsafe compiler switch needed). Safety comes from adhering to the contract, not from language design (although a language can and should make coding in a style that adheres to the contract as easy as possible, and provide warning when the contract is violated as much as possible.) – Ben Voigt
upvote
  flag
Nice explanation Eric. Quick question! Which language would you say is a safer language?! – Bitmap
26 upvote
  flag
@Bitmap: LOGO is quite safe. – Ben Voigt
5 upvote
  flag
@Ben well duh, obviously there are ways to become unsafe, which include library functions marked as such (so permissions kick in if required). If someone did a LOGO implementation with a library function allowing intptr moral equivalents then it would stop being safe by your metric too. – ShuggyCoUk
33 upvote
  flag
Strictly speaking the analogy should mention that the receptionist at the hotel was quite happy for you to take the key with you. "Oh, do you mind if I take this key with me?" "Go ahead. Why would I care? I only work here". It doesn't become illegal until you try to use it. – philsquared
1 upvote
  flag
@PhilNash: It breaks down a little there, as the key is usually the property of the hotel. – Lightness Races in Orbit
10 upvote
  flag
@Ben: @ShuggyCoUk is right; that there are library functions that do horrible things if you misuse them is a property of those library functions, not the C# language. C# the language is both memory safe and type safe provided that you don't have "unsafe" code blocks in there. If you do, then it is every bit as memory-unsafe as C++. The point is to isolate areas of memory unsafety to areas that can be easily identified and thoroughly reviewed. – Eric Lippert
25 upvote
  flag
So this means the bible is a static object? I mean, it never leaves the room. – alesko
8 upvote
  flag
@Kyle Cronin your point only furthers the analogy. Back when C++ was invented, programmable card keys for hotels were less common or even nonexistent. Newer hotels have naturally adopted safer practices, as have newer languages. Even older hotels have been retrofitted with new locks, as has C++ (smart pointers anybody?) – Mark Ransom
upvote
  flag
C++ not being memory safe makes it pragmatic. Some tricks can be used, and those hacks would not be there if C++ was too safe. – Thaddee Tyl
20 upvote
  flag
@Thaddee: First off, there are plenty of pragmatic languages that are memory safe. However, the problem with C++ is not that it is unsafe. The problem it is that it is so easy to accidentally do something massively unsafe and not realize that you're doing so until a you crash the end-user's machine. Memory-unsafe languages often are quite useful, I agree, but there should be a way of isolating that unsafeness to specifically those "tricky, hacky" bits of code that really need it. – Eric Lippert
5 upvote
  flag
Eric: This question might be getting traffic because it was the top post on Hacker News: news.ycombinator.com/item?id=2686580. Regardless, 1100 upvotes in 24 hours?! That must be a record, by far. – Steve Tjoa
108 upvote
  flag
Please, please at least consider writing a book one day. I would buy it even if it was just a collection of revised and expanded blog posts, and I'm sure so would a lot of people. But a book with your original thoughts on various programming-related matters would be a great read. I know that it's incredible hard to find the time for it, but please consider writing one. – Dyppl
13 upvote
  flag
@Dyppl: Thanks for the kind words. Having written a couple of books already I am well aware of how much work it is! I have considered turning the blog into a book and I might at some point if I can find both the time and a willing publisher. – Eric Lippert
upvote
  flag
Actually there are three basic techniques for managing memory in C and C++. There are the two that you mentioned plus static memory where the variables have process lifetime. And if you don't mind getting very technical there is also register file storage, but this is apparently ignored by current compilers. – ThomasMcLeod
upvote
  flag
So what exactly is the frequency of finding that book every time I sneak into the same room? Also, on what factors does this frequency depend on? – chosentorture
4 upvote
  flag
@chosentorture answer your question with science. Get a few hundred c compilers and try a few hundred different configurations of each and soon you will have excellent empirical data. Anything else is guessing. – Eric Lippert
upvote
  flag
I have written a lot of code in a lot of different languages. My least favorite language has been C++. I have no issues with unsafe languages - what I have issues with is a language so poorly designed (and then hacked upon to cover up those design flaws) that it is FAR TOO EASY to create code that segfaults. I'm dealing with an issue right now that was SUPPOSED to fix memory leaks. Now it segfaults. Fun. -_- – Lloyd Sargent
upvote
  flag
Does the same also apply to local variables inside the same function, that are declared in a different scope? I'm asking since in my experience both GCC and MSVC do a) not warn (even with -Wextra) about using a pointer to variable in a different scope, and b) create assembly that suggests they track the usage of every variable through pointers even beyond the variable's scope. example: void foo(void){ int i, *px; for (i=0;i<10;i+=*px) {int x=i+1; px=&x;} printf("*px=%i\n", *px); } – Timo
2 upvote
  flag
@timo you are required to never use an address to a local whose lifetime has ended. If you do and it happens to work, well, again, the runtime is not required to fail when you break the rules. Unsafe code is marked unsafe for a reason. – Eric Lippert
upvote
  flag
@EricLippert I think this is best analogy that I've ever seen related this topic but I have one confusion you wrote that "yours could be in the hotel's furnace" it means my value could be there in other location in the system or something else you try to explain ? – Vikas Verma
4 upvote
  flag
@VikasVerma: Some memory managers deliberately shred memory when it is no longer usable. The debug version of the Microsoft C runtime, for example, sets unused memory to 0xCC because (1) it is very easy to see in the debugger memory window that a particular block of memory is now no longer valid, and (2) that is the "break into the debugger" instruction code; if the shredded memory ever gets executed then the debugger will be activated. – Eric Lippert
upvote
  flag
I actually disagree with this as 'answer' after being sent the link to it, in that it didn't answer the clear cause of confusion for the poster. He clearly thinks that, similar to an object, a function contains its own local storage and therefore doesn't exist after the function is "destroyed". Only it's not actually destroyed. Unlike classes, they aren't a container for the variables used in them, they're items stored on the stack or in a register. However, this answer is a great analogy on how manipulated access of the stack can (and can't) work. Technical answer is more important, though. – Deji
6 upvote
  flag
@Deji: Your psychic powers are much stronger than mine; I have no idea what the original poster was thinking. – Eric Lippert
upvote
  flag
@ErricLippert It's not psychic powers, but more familiarity with the confusion, the example he's using and the actual question he asked. He asked if the memory was inaccessible, which means he likely thinks the memory doesn't exist any more. Both are untrue, the memory is accessible and it does exist. The reason to which is the difference in the way these are stored, which is why I think the 'answer' ought to focus on that on a technical level. – Deji
2 upvote
  flag
@VikasVerma It means that your drawer is being reconstructed. – joey rohan
upvote
  flag
@EricLippert: Thanks for the great answer. What is your opinion about C++11 & C++14 way of free store management using smart pointers? Can I now say that modern C++ is safe language because there is no need to use delete operator – Destructor
upvote
  flag
@meet: I am not an expert by any means on what has been added to C++ 11 and 14, though in talking with people who are experts, it sounds to me like there is a lot of good stuff in there. More generally I am happy to see that the C++ committee is willing to be both bold and active as they move the language towards something more modern and less error-prone. – Eric Lippert
upvote
  flag
@EricLippert: Ok. But question is why C++ won't stop me if I do something foolish? Wouldn't it be very nice if compiler gives me error when I attempt to take address of local variable? Why C++ provides so much freedom to the programmer? Or these are the problems C++ inherited from C? Your help will be appreciated. – Destructor
6 upvote
  flag
@meet: you should ask these questions of someone who is an expert in the design of C++; I would not care to speculate as to the motives of the C++ language designers. I would note that "stop the user from doing something foolish" does not appear to have been too high on the list of traits considered admirable by the designers of C. – Eric Lippert
upvote
  flag
@Meet Remember that C++ is a general propuse language. Total memory control is needed for a wide range of applications (cracking tools). – Edwin Rodríguez
upvote
  flag
@EricLippert: C++ doesn't define this behaviour, true. But I think that if you are running on x86, the architecture guarantees that you can safely write and read up to 128 bytes above the stack pointer (esp), without risking that the memory changes. Now, if the compiler doesn't compile any instructions that actively modify that memory (which is probably the case, since it will just increase esp and jump back when leaving the function), I think you could technically say that on x86, this is defined behaviour. Is this true? Any thoughts on that? – Martijn Courteaux
upvote
  flag
@MartijnCourteaux: Who says that the compiler is required to use esp to determine the locations of local variables? If a particular compiler vendor defines the behaviour for a particular implementation, then the behaviour is implementation defined. – Eric Lippert
upvote
  flag
@EricLippert: I don't see where you are going. Could it be true that on a specific architecture/compiler combo, this results in consistent behaviour, even if the local variables now sit above the stackpointer (&var < esp)? For example: i tried x86_64 with gcc -O0, and it produces code that I think will give consistent results. – Martijn Courteaux
upvote
  flag
@MartijnCourteaux: Undefined behavior can do anything. Consistent behavior is a subset of anything, so yes, that is possible. Where I am going is: you are asking whether behaviour that is defined by a particular implementation is a kind of implementation-defined behavior. Yes, it is. – Eric Lippert
upvote
  flag
In the last paragraph before your update you say something to the effect of "C++ is not a safe language[...] Safer languages like C++[....]" Did you mean to say C# is safer? – canon
upvote
  flag
It would be nice if the answer at least once mentioned word undefined behaviour – Giorgi Moniava
upvote
  flag
@GiorgiMoniava: Comment noted. Consider writing an answer you like better; that way the whole site is improved. – Eric Lippert
upvote
  flag
@EricLippert Your answer is already pretty good, I don't attempt to criticize it, just wrote my opinion. Thanks. It won't be easy/realistic to write something better than current answers here. – Giorgi Moniava
upvote
  flag
"billions of nanoseconds" huehue. gotta remember that one – Hubert Grzeskowiak
3 upvote
  flag
I have to agree with @Dyppl that I would like to read a book written by you. Along with some of the blog posts/answers written by the jOOQ crew, or Josh Bloch/Goetz, your answers provide a really detailed and easily understandable material on behind the scenes/under the hood details regarding programming languages – Abdul

Your problem has nothing to do with scope. In the code you show, the function main does not see the names in the function foo, so you can't access a in foo directly with this name outside foo.

The problem you are having is why the program doesn't signal an error when referencing illegal memory. This is because C++ standards does not specify a very clear boundary between illegal memory and legal memory. Referencing something in popped out stack sometimes causes error and sometimes not. It depends. Don't count on this behavior. Assume it will always result in error when you program, but assume it will never signal error when you debug.

upvote
  flag
I recall from an old copy of Turbo C Programming for the IBM, which I used to play around with some way back when, how directly manipulating the graphics memory, and the layout of the IBM's text mode video memory, was described in great detail. Of course then, the system that the code ran on clearly defined what writing to those addresses meant, so as long as you didn't worry about portability to other systems, everything was fine. IIRC, pointers to void were a common theme in that book. – Michael Kjörling
upvote
  flag
@Michael Kjörling: Sure! People like to do some dirty work once in a while ;) – Chang Peng

What you're doing here is simply reading and writing to memory that used to be the address of a. Now that you're outside of foo, it's just a pointer to some random memory area. It just so happens that in your example, that memory area does exist and nothing else is using it at the moment. You don't break anything by continuing to use it, and nothing else has overwritten it yet. Therefore, the 5 is still there. In a real program, that memory would be re-used almost immediately and you'd break something by doing this (though the symptoms may not appear until much later!)

When you return from foo, you tell the OS that you're no longer using that memory and it can be reassigned to something else. If you're lucky and it never does get reassigned, and the OS doesn't catch you using it again, then you'll get away with the lie. Chances are though you'll end up writing over whatever else ends up with that address.

Now if you're wondering why the compiler doesn't complain, it's probably because foo got eliminated by optimization. It usually will warn you about this sort of thing. C assumes you know what you're doing though, and technically you haven't violated scope here (there's no reference to a itself outside of foo), only memory access rules, which only triggers a warning rather than an error.

In short: this won't usually work, but sometimes will by chance.

It works because the stack has not been altered (yet) since a was put there. Call a few other functions (which are also calling other functions) before accessing a again and you will probably not be so lucky anymore... ;-)

The things with correct (?) console output can change dramatically if you use ::printf but not cout. You can play around with debugger within below code (tested on x86, 32-bit, MSVisual Studio):

char* foo() 
{
  char buf[10];
  ::strcpy(buf, "TEST”);
  return buf;
}

int main() 
{
  char* s = foo();    //place breakpoint & check 's' varialbe here
  ::printf("%s\n", s); 
}

You actually invoked undefined behaviour.

Returning the address of a temporary works, but as temporaries are destroyed at the end of a function the results of accessing them will be undefined.

So you did not modify a but rather the memory location where a once was. This difference is very similar to the difference between crashing and not crashing.

It can, because a is a variable allocated temporarily for the lifetime of its scope (foo function). After you return from foo the memory is free and can be overwritten.

What you're doing is described as undefined behavior. The result cannot be predicted.

That's classic undefined behaviour that's been discussed here not two days ago -- search around the site for a bit. In a nutshell, you were lucky, but anything could have happened and your code is making invalid access to memory.

This behavior is undefined, as Alex pointed out--in fact, most compilers will warn against doing this, because it's an easy way to get crashes.

For an example of the kind of spooky behavior you are likely to get, try this sample:

int *a()
{
   int x = 5;
   return &x;
}

void b( int *c )
{
   int y = 29;
   *c = 123;
   cout << "y=" << y << endl;
}

int main()
{
   b( a() );
   return 0;
}

This prints out "y=123", but your results may vary (really!). Your pointer is clobbering other, unrelated local variables.

A little addition to all the answers:

if you do something like that:

#include<stdio.h>
#include <stdlib.h>
int * foo(){
    int a = 5;
    return &a;
}
void boo(){
    int a = 7;

}
int main(){
    int * p = foo();
    boo();
    printf("%d\n",*p);
}

the output probably will be: 7

That is because after returning from foo() the stack is freed and then reused by boo(). If you deassemble the executable you will see it clearly.

2 upvote
  flag
Simple, but great example to understand the underlying stack theory.Just one test addition, declaring "int a = 5;" in foo() as "static int a = 5;" can be used to understand the scope and life time of a static variable. – control
8 upvote
  flag
-1 "for will probably be 7". The compiler might enregister a in boo. It might remove it because it's unnecessary. There is a good chance that *p will not be 5, but that doesn't mean that there is any particularly good reason why it will probably be 7. – Matt
5 upvote
  flag
+1 as it explains the situation in question while stating it's not guaranteed. – rahmanisback
2 upvote
  flag
It is called undefined behavior! – Francis Cugler
upvote
  flag
why and how boo reuses the foo stack ? aren't function stacks separated from each other, also I get garbage running this code on Visual Studio 2015 – ampawd
upvote
  flag
@ampawd it's almost a year old, but no, "function stacks" are not separated from each other. A CONTEXT has a stack. That context uses its stack to enter main, then descends into foo(), exists, then descends into boo(). Foo() and Boo() both enter with the stack pointer at the same location. This isn't however, behavior that should be relied upon. Other 'stuff' (like interrupts, or the OS) can use the stack between the call of boo() and foo(), modifying it's contents... – Russ Schultz

Pay attention to all warnings . Do not only solve errors.
GCC shows this Warning

warning: address of local variable 'a' returned

This is power of C++. You should care about memory. With the -Werror flag, this warning becames an error and now you have to debug it.

This is definitely a timing issue! The object that the p pointer points to is "scheduled" to be destroyed when if goes out of foo's scope. This operation however, does not happen immediately, but rather a number of CPU cycles later. Whether this is undefined behavior, or C++ is actually doing some pre-cleanup stuff in the background, I don't know.

If you insert a call to your operating system's sleep function between the call to foo and the cout statements, making the program wait a second or so before dereferencing the pointer, you will notice that the data is gone by the time you want to read it! Look at my example:

#include <iostream>
#include <unistd.h>
using namespace std;

class myClass {
public:
    myClass() : i{5} {
        cout << "myClass ctor" << endl;
    }

    ~myClass() {
        cout << "myClass dtor" << endl;
    }

    int i;
};

myClass* foo() {
    myClass a;
    return &a;
}

int main() {

    bool doSleep{false};

    auto p = foo();

    if (doSleep) sleep(1);

    cout << p->i << endl;
    p->i = 8;
    cout << p->i << endl;
}

(Notice that I used the sleep function from unistd.h, which is only present on Unix-like systems, so you will need to replace that with Sleep(1000) and Windows.h if you're on Windows.)

I replaced your int with a class, so I can see exactly when the destructor is called.

The output of this code is the following:

myClass ctor
myClass dtor
5
8

However, if you change doSleep to true:

myClass ctor
myClass dtor
0
8

As you can see, the object that is supposed to be destroyed IS actually destroyed, but I suppose there are some pre-cleanup instructions that must execute before an object (or just a variable) gets destroyed, so until those are done, the data is still accessible for a short period of time (however there's no guarantee for that of course, so please don't write code that relies on this).

This is very weird, since the destructor is called immediately upon exiting the scope, however, the actual destruction is slightly delayed.

I never really read the part of the official ISO C++ standard that specifies this behavior, but it might very well be, that the standard only promises that your data will be destroyed once it goes out of scope, but it doesn't say anything about this happening immediately, before any other instruction is executed. If this is the case, than this behavior is completely fine, and people are just misunderstanding the standard.

Or another cause could be cheeky compilers that don't follow the standard properly. Actually this wouldn't be the only case where compilers trade a little bit of standard conformance for extra performance!

Whatever the cause of this is, it's clear that the data IS destroyed, just not immediately.

6 upvote
  flag
Variables aren't "scheduled" to be destroyed. The compiler will just leave the memory as-is until something else writes to it. The reason the memory is overwritten with 0 is because the sleep function uses that memory; not because the memory is destroyed for some reason. – NobodyNada
3 upvote
  flag
Uninformed, presumptuous nonsense. C/C++ don't perform garbage collection as it would totally cripple performance for good programmers who don't do stupid things like trying to continue using destructed objects (and anyone who cares can implement it themselves). "I never really read the part of the official ISO C++ standard that specifies this": you seemingly didn't read any relevant bit as there is none; you are daydreaming falsehoods. I'm amazed by how you then take this and translate 'I have not bothered to research whether this exists' into 'This does exist, but who knows how it works lol' – underscore_d
upvote
  flag
Look, the words I used may not be the most appropriate, but my answer does prove to be true. Of course C++ doesn't have garbage collection, that's not what I meant. But when an object goes out of scope, it is destroyed, don't you agree? That sure happens. But if you try to access the memory associated with it just after exiting the scope, it might still be there, because it hasn't actually been freed in practice yet. That was the point of my answer, and the code I showed does prove this as well. But if you have a better answer, I (as well as the OP) would be happy to see it. – adam10603
4 upvote
  flag
@adam10603: Unfortunately, that is complete nonsense. I appreaciate your willingness to help, but unfortunately not a bit of what you've written is correct. – andreee

It's 'Dirty' way of using memory addresses. When you return an address (pointer) you don't know whether it belongs to local scope of a function. It's just an address. Now that you invoked the 'foo' function, that address (memory location) of 'a' was already allocated there in the (safely, for now at least) addressable memory of your application (process). After the 'foo' function returned, the address of 'a' can be considered 'dirty' but it's there, not cleaned up, nor disturbed/modified by expressions in other part of program (in this specific case at least). A C/C++ compiler doesn't stop you from such 'dirty' access (might warn you though, if you care). You can safely use (update) any memory location that is in the data segment of your program instance (process) unless you protect the address by some means.

After returning from a function, all identifiers are destroyed instead of kept values in a memory location and we can not locate the values without having an identifier.But that location still contains the value stored by previous function.

So, here function foo() is returning the address of a and a is destroyed after returning its address. And you can access the modified value through that returned address.

Let me take a real world example:

Suppose a man hides money at a location and tells you the location. After some time, the man who had told you the money location dies. But still you have the access of that hidden money.

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