Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
591 views
in Technique[技术] by (71.8m points)

c++ - 是否可以在其范围之外访问局部变量的内存?(Can a local variable's memory be accessed outside its scope?)

I have the following code. (我有以下代码。)

#include <iostream>

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

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

And the code is just running with no runtime exceptions! (而且代码没有任何运行时异常就可以运行!)

The output was 58 (输出为58)

How can it be? (怎么可能?) Isn't the memory of a local variable inaccessible outside its function? (局部变量的存储不是在其功能之外不可访问的吗?)

  ask by community wiki translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 . (C ++不是安全的语言 。) 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. (如果您试图做一些非法和愚蠢的事情,例如回到没有权限的房间,或者翻阅一张桌子甚至翻腾的桌子,那么C ++不会阻止您。) Safer languages than C++ solve this problem by restricting your power -- by having much stricter control over keys, for example. (比C ++更安全的语言通过限制您的力量来解决此问题-例如,通过对键进行更严格的控制。)

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 method is to have a “short-lived” storage area where the lifetime of each byte is well known. (第二种方法是拥有一个“短暂的”存储区域,其中每个字节的生存期众所周知。) Here, the lifetimes follow a “nesting” pattern. (在此,生命周期遵循“嵌套”模式。) The longest-lived of these short-lived variables will be allocated before any other short-lived variables, and will be freed last. (这些短期变量中寿命最长的变量将在任何其他短期变量之前分配,最后释放。) Shorter-lived variables will be allocated after the longest-lived ones, and will be freed before them. (寿命较短的变量将在寿命最长的变量之后分配,并在它们之前被释放。) The lifetime of these shorter-lived variables is “nested” within the lifetime of longer-lived ones. (这些寿命较短的变量的寿命被“嵌套”在寿命较长的变量的寿命之内。)

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; (不需要使用C ++的实现就可以使用堆栈来存储本地对象;) 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; (不需要C ++的实现就可以使您留在堆栈上的垃圾保持不变,以便以后可以非法返回它;) 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. (不需要C ++的实现来确保在逻辑上缩小堆栈时,曾经有效的地址仍会映射到内存中。) 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. (在“普通” C#中,根本没有办法获取本地地址并将其返回或存储以供以后使用。) 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: (进一步阅读:)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...