酒店房间和 C++ 局部变量的作用域

本文由 伯乐在线 - 菜鸟浮出水 翻译自 StackOverflow。欢迎加入 技术翻译小组。转载请参见文章末尾处的要求。

问题:Can a local variable’s memory be accessed outside its scope? 有一段局部变量的内存,可以从其范围之外访问它么?

如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
int *foo()
{
    int a = 5;
    return &a;
}
int main()
{
    int *p = foo();
    cout << *p;
    *p = 8;
    cout << *p;
}

这样的代码可以正常执行,而且没有任何运行时的异常!

输出是 5 8

这是怎么回事?难道局部变量在函数外也可以被访问吗?

 

来自微软资深软件工程师 Eric Lippert 的最佳答案(3200+赞):

你在酒店里租了一间房。你把一本书放进了桌子的第一个抽屉里,然后就去睡觉了。当你第二天早上醒来时,你假装忘记去还钥匙了。你偷了房间的钥匙!

一周之后,你回到了酒店,但没有入住,你用偷来的钥匙溜进了你上次入住的房间,并查看了那个抽屉。你的书还在那里。是不是很令人吃惊!

这是怎么回事呢?难道一个酒店房间的抽屉不是应该无法被一个没有入住这个房间的人看到吗?

好吧,明显的是,这种情况在真实世界中当然会发生。在你不入住这个房间的时候,这里面没有任何神秘的力量把你的书弄消失掉,也没有魔法能够阻止你用偷来的钥匙进入房间。

酒店的管理规章里没有要求拿走你的书。你也没有跟他们说如果你落下了一本书,他们可以帮你撕毁它。如果你用偷来的钥匙非法进入了你上次的房间,并且没有被酒店的安保系统发现。你也没有跟他们说如果你之后尝试溜进房间,他们应该阻止你。不过事实上,你确实签了一份协议规定你保证不会偷偷溜回房间。只不过你打破了协议。

在这种情况下任何事情都有可能发生。如果你运气好的话,那本书可能还在那里。其他人的书也可能在那个抽屉里而你的书则被丢进了酒店的火炉里。也可能当你溜进去的时候正好有个人在把你的书撕成碎片。酒店可能把那张桌子连带你的书都移走了,而把一个衣柜放在那里。这家酒店也可能正好要被拆除,换成一个足球场,在你溜来溜去的时候。你可能会在一场爆破中死去。

当你离开酒店而偷了房间的钥匙的时候,你不知道将会发生什么。你放弃了去生活在一个可靠的,安全的世界里,因为你选择去打破系统的规则。

C++不是一门安全的语言。你可以非常轻松就打破这个系统的规则。如果你尝试去做一些非法并且愚蠢的事情,比如你回到那个你已经不入住的房间,并想要去查看那张也许已经不存在的桌子。C++不会阻止你的。比C++更加安全的语言通过限制你的能力来解决这个问题,比如通过更加严格的控制房间钥匙。

【更新】:

我的老天。这个答案获得了这么多的关注。(我不知道为什么,我只是觉得这样比喻比较有趣, 不过管他呢。)

我认为在经过了更加技术性的思考之后更新一下这个答案是必要的。

编译器的工作是生成代码来管理这个程序数据拥有的内存。有很多方式来生成管理内存的代码,但是这么多年来有两个基本的技术是必须要知道的。

第一个是拥有一片长期存在的区域,这片存储区域里的每一个字节,他们的生命周期比较长。生命周期的意思就是它们能够被程序访问的时期。这类内存没办法提前进行预估。编译器生成一种叫堆管理器的代码,它知道如何在需要的时候动态的分配内存,当内存不再被需要的时候释放掉他们。

第二个是拥有一片短期存在的区域,这片存储区域里的每一个字节都可以提前进行预估。而且比较特殊的是,这片区域的生命周期遵循一种嵌套模式。也就是说,在这片区域中拥有最长生命周期的变量,它所分配的内存地址被它之后分配的那些生命周期较短的变量所重用。

局部变量就是第二种情况。当调用一个函数时,它的局部变量便被生成了。当这个函数调用另外一个函数时,新函数的局部变量也被生成了。这些变量会在第一个函数的局部变量之前被释放掉。这些局部变量的内存地址的开始和结束可以提前被计算出来。

因为这个原因,局部变量经常被分配到栈数据结构里,因为一个栈的特点是第一个入栈的元素将会最后一个出栈。

这就好像酒店决定只能按照顺序进行房间的出租。你没办法离开,除非你之前所有房间号比你大的人都走了。

所以,让我们来想一下栈的操作过程。在很多操作系统中,每一个线程都有一个栈,并且栈的大小是一个可变的确定大小。当你调用一个函数的时候,相关的内容被压入栈内。当你把一个这个栈的指针传出这个函数时,就像上面的提问者所干的一样。那个指针只是指向全部有效的数百万个字节内存块的中间。在我们的类比中,当你离开酒店的时候,你只是离开了当前被占用的数字最大的房间。如果没有人在你之后入住,你又非法地回到了这个房间。你所有的东西肯定都还在这个酒店的房间里。

我们用栈作为临时存储因为它们非常廉价并且容易实现。C++的实现没有规定一定要用栈来存储局部变量,你可以使用堆来存储它们,不过没有人这么干,因为那样做会使得程序变得很慢。

C++也没有规定在你离开栈之后需要清掉栈里的内容,所以你可以在之后非法地回到栈里找到你之前的内容。当然编译器如果生成代码,一旦你不再使用了就把栈里的所有内容都清零,这是完全合法的。不需要再解释为什么了,因为这样做代价非常高。

C++没有规定要确保当栈变小时,之前有效的内存地址依然有效。C++的实现也允许告诉操作系统“我们已经不再需要栈的个内存页了。除非我说,否则当有任何人要访问这个之前有效的栈的内存页的时候抛出一个异常并结束程序”。再次,一般的实现也没有这么做,因为这么说使程序变慢而且没有必要。

相反,大多数时候,一般的C++实现允许你犯错然后避免它。直到有一天,一些真正非常令人恐怖的错误出现了然后把整个程序弄崩溃了。

这样做是有问题的。C++里有如此多的规则而又如此轻易就可以打破它们。我自己就有好多次这样的经历。更糟的是,这种问题往往是表面的,当你发现内存地址冲突了之后去检查内存,却发现它们在很长时间内又是正确的。所以你很难知道到底是哪个地方出错了。

那些内存安全的语言通过限制你的能力来解决这个问题。在规范的C#里,没有任何办法去获取一个局部变量的内存地址,然后返回它或者是存储它等以后再用。你可以获取一个局部变量的内存地址,但是语言被很好的设计了,你不可能在局部变量生命周期之后还能够使用它。为了取得局部变量的内存地址并把它返回,你必须要把编译器设置为一个特殊的不安全的模式,并且在你的程序里写上“unsafe”关键字。这可以帮助提醒你,你正在做一些不安全的可能会打破规则的事情。

更进一步阅读:
当C#返回引用时做了些什么?

http://blogs.msdn.com/b/ericlippert/archive/2011/06/23/ref-returns-and-ref-locals.aspx

为什么我们用栈来管理内存?C#里值的类型是否一直存储在栈里?虚拟内存是如何工作的?以及更多的关于C#内存管理是如何工作的。这里许多文章都对C++程序员有帮助。

http://blogs.msdn.com/b/ericlippert/archive/tags/memory+management/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/447555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决:Access denied for user ‘root’@‘localhost’(using password: YES)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 我报这个错的原因很简单&#xff1a;数据库连接时密码输入错误。把密码确认对了&#xff0c;就连接成功了。

【历史回顾】Linux发展一览

我们周围到处都有Linux的身影&#xff0c;在家中、公司里、大学、实验室&#xff0c;太空空间站。现在的Linux已经从一个个人爱好发展成了一场计算机革命。在这篇文章里&#xff0c;我们向你展示最完整的Linux 23年来发展的历史年表。我们周围到处都有Linux的身影&#xff0c;在…

专访联想谢政维:功耗和价格是天蝎项目最大障碍!

摘要&#xff1a;与国外OpenStack和OCP&#xff08;开放计算项目&#xff09;频频见诸报端不同&#xff0c;中国的天蝎计划看起来神神秘秘&#xff0c;今天专访联想谢政维&#xff0c;他帮我们解密了目前天蝎项目的一些基本情况&#xff0c;以及他对未来天蝎发展的观点。 服务…

解决:Chrome 插件安装时提示 程序包无效:“CRX_HEADER_INVALID“

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 今天在添加谷歌插件的时候&#xff0c;却发现谷歌浏览器显示 程序包无效&#xff1a;"CRX_HEADER_INVALID"&#xff0c;现整理…

65种GPU性能测试,AMD开源驱动领先!

摘要&#xff1a;近日&#xff0c;Phoronix测试了65 种不同的GPU使用开源驱动的OpenGL性能&#xff0c;测试的GPU型号包括Intel HD Graphics、AMD Radeon、AMD FirePro和NVIDIA GeForce系列。结果显示&#xff0c;相比之下&#xff0c;AMD开源驱动领先于NVIDIA。 【编者按】20…

1分钟看懂:java 项目中 VO 、DTO、Entity,各自是在什么情况下应用的

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1、entity 里的每一个字段&#xff0c;与数据库相对应&#xff0c; 2、vo 里的每一个字段&#xff0c;是和你前台 html 页面相对应&…

指尖上的艺术——如何运用代码发挥无限创意

摘要&#xff1a;计算机为我们的生活带来了不计其数的变化&#xff0c;现在&#xff0c;我们不妨也把它与艺术结合起来&#xff0c;去从机器的角度来为生活增添活力与色彩&#xff0c;开拓一个无限可能的艺术创作新境界。 写在前面 生成的艺术&#xff0c;指尖上的艺术&#…

解决:XXX类存在 却报错 Unkonwn class:‘XXX‘ 、@Autoeired 注入失败

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. 我的情况如图&#xff0c;有一个CuxxxxManager 类明明是就在本工程中的&#xff0c;我想注入到对应 Service 中&#xff0c;却直接报…

精益设计:“纸上谈兵”的说服力

摘要&#xff1a;当你和团队通宵达旦地输出大量完整而精美的方案时&#xff0c;可客户却根本不买帐。这究竟是沟通出了问题&#xff0c;还是我们犯了错&#xff1f;如何提高设计效率、提升沟通及如何设计出更好的用户体验是每个互联网从业者都值得关注的问题。 【编者按】此前…

《西线无战事》:合上书的那一刻:只想痛哭

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 昨晚看完此书&#xff0c;看的时候&#xff0c;我一直以为他是在回忆&#xff0c;然而&#xff0c;最终却没有一个人活下来&#xff0c;…

如何兼职创业并避免风险

大多数人开始创建第一家公司时&#xff0c;仍然有一份正常工作。 这是有道理的&#xff1a;你不需要贷款&#xff0c;并且你并不需要资金。如果你“失败”了&#xff0c;你失去仅仅是时间&#xff0c;但考虑到你得到的乐趣&#xff0c;经历&#xff0c;以及一切你已经学会的东西…

得当前时间的工具类

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Date datenew Date();DateFormat formatnew SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String timeformat.format(date);不同…

8款最受欢迎的HTML5/CSS3应用及代码

新的一周开始&#xff0c;小编也将继续为大家分享精彩的HTML5应用&#xff0c;还有CSS3和jQuery方面的东西。今天给大家带来的是8款最受欢迎的HTML5/CSS3应用及代码&#xff0c;一起来看看吧。 1、基于HTML5 Canvas的图表插件Chart.js chart.js是一款基于HTML5 Canvas的图表插…

Java 8: LocalDate、LocalTime 、LocalDateTime 处理日期时间

JDK8中&#xff0c;新增了三个类&#xff0c;用以处理时间。 LocalDate专门处理日期&#xff0c;LocalTime专门处理时间&#xff0c;LocalDateTime包含了日期和时间&#xff0c;而且对于很多复杂的问题&#xff0c;都提供了现成的方法&#xff0c;比如&#xff1a;获取2017年1…

超负荷写代码 = 慢性自杀

本文是html5tricks原创翻译&#xff0c;转载请看清文末的转载要求&#xff0c;谢谢合作&#xff01; 也许你会认为我是个故弄玄虚的标题党&#xff0c;不过我需要澄清一下。首先&#xff0c;这是真的&#xff0c;超负荷写代码的确意味着慢性自杀。然后问题就来了&#xff0c;“…

Dubbo + RestEasy 实现文件上传与下载

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 DubboRestEasy实现文件上传与下载 DubboRestEasy实现文件上传与下载 Maven依赖 注解 代码实现 1、 上传 2、 下载 3、 上传、下载请求参…

程序员必须知道的10大基础实用算法及其讲解

算法一&#xff1a;快速排序算法 快速排序是由东尼霍尔所发展的一种排序算法。在平均状况下&#xff0c;排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较&#xff0c;但这种状况并不常见。事实上&#xff0c;快速排序通常明显比其他Ο(n log n) 算法更快&a…

《 双城记 》:无数的平民拥有的只是和她同样的眼睛 ...

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 PS&#xff1a;此书我没有买&#xff0c;是在喜玛拉雅上听完的&#xff0c;主播&#xff1a;高乐高 。 1、卡顿&#xff1a; 一个才华斐…

Git远程操作详解

摘要&#xff1a;Git是目前最流行的版本管理系统&#xff0c;学会Git几乎成了开发者的必备技能。本文详细介绍5个Git命令&#xff0c;它们的概念和用法&#xff0c;理解了这些内容&#xff0c;你就会完全掌握Git远程操作。 【编者按】Git是一个分布式版本控制&#xff0f;软件…

《千只鹤》:面对爱,我们永远还行走在成长的路上 ...

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 PS&#xff1a;之前我一直以为书名是 千纸鹤&#xff0c;直到真正拿到书才注意到&#xff0c;原来书名是《千只鹤》... 包括 2 部分&am…