C++ 局部变量的作用域 --- 以酒店房间为比喻说明

C语言入门到精通:从翁恺课程到项目实战的完整学习路径 C语言作为计算机编程的基石,其核心在于理解内存管理、指针操作和底层系统交互。通过掌握变量、数据类型、函数和流程控制等基础语法,学习者能够构建稳固的编程心智模型。指针作为C语言的灵魂,是实现高效内存操作和数据结构的關鍵,理解指针与数组、函数参数传递及动态内存分配的原理,是进阶系统编程和嵌入式开发的必备技能。在工程实践中,通过开发通讯录管理、文本词频统计等项目,能深化对文件操作、结构体应用的理解。结合翁恺老师的经典课程,从内存视角剖析语法本质,配合GCC编译器、GDB调试器等工具链的使用,可系统性地跨越从语法学 阅读详情

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

如下代码:

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://blog.jobbole.com/69923/

避坑指南:用逐飞开源库驱动MT9V03X摄像头时容易忽略的3个细节(附TFT显示优化技巧) 本文详细解析了在智能汽车竞赛中使用MT9V03X摄像头(总钻风)时常见的硬件连接、图像采集和TFT显示优化问题。重点介绍了逐飞开源库中容易被忽略的三个关键细节,包括电源管理、看门狗设置和中断优先级配置,并提供了针对IPS200和TFT180显示屏的优化技巧,帮助参赛队伍提升图像处理效率和显示质量。 阅读详情

相关推荐

别再被SpringerLink虐了!手把手教你搞定LaTeX源文件提交(附正确文件顺序和类型选择)

本文详细解析了SpringerLink投稿系统中LaTeX源文件提交的常见问题与解决方案,包括文件打包、类型选择和顺序排列等关键步骤。通过实战经验,帮助科研人员避开隐形陷阱,一次性完成LaTeX提交,提升投稿效率。特别针对`.bst`和`.cls`文件的正确处理提供了专业指导。

weixin_42522964的博客 445

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

本文由 伯乐在线 - 菜鸟浮出水 翻译自 StackOverflow。欢迎加入技术翻译小组。转载请参见文章末尾处的要求。 问题:Can a local variable’s memory be accessed outside its scope? 有一段局部变量的内存,可以从其范围之外访问它么? 如下代码: 1 2 3 4 5 6 7 8

szu030606的专栏 2136

运维与软件实施课表

运维与软件实施课表 课程 章节编号 章节 课程地址 软件及操作系统基础 ch01 软件及操作系统基础01 实施、运维工程师基本技能:https://blog.youkuaiyun.com/qq_28189423/article/details/82700539 老毛桃制作winPE镜像:https://blog.youkuaiyun.com/qq_2818942...

Mr.O~的博客 1万+

C语言全局变量,局部变量及其存储类别(关键字),作用域

本次详细讲解了变量的相关知识,干货多多

weixin_73604097的博客 8799

计算机考研复试面试常问问题 数据结构篇(下)

计算机考研复试面试常问问题 数据结构篇(下) 使用前需知(拒绝白嫖,如果对你有帮助,你只需点个赞就行): 注意:有人在闲鱼上盗卖我的资料,而且还有很多同学买了,请各位擦亮双眼,我是整理出来免费分享给大家的! 大家请多点赞和评论,这样后面的同学更容易百度到,不至于被人盗卖,我就不加水印影响大家使用了! 第一次整理,度不好控制,有不好的地方希望大家理解,后面可能会改进,造福今年之后的同学们! ...

程序员宝藏的博客 1万+

STM32 HAL PWM (占空比) -电机软启动不到位解决

为了避免电机启动电压大造成设备损坏,或者供电不足导致重启,通常在对启动都需要一个PWM进行软启动,下面对PWM电机软启动详细介绍。

weixin_44597885的博客 868

C++ 变量作用域

http://www.runoob.com/cplusplus/cpp-variable-scope.html 作用域是程序的一个区域,一般来说有三个地方可以定义变量: 在函数或一个代码块内部声明的变量,称为局部变量。 在函数参数的定义中声明的变量,称为形式参数。 在所有函数外部声明的变量,称为全局变量。 我们将在后续的章节中学习什么是函数和参数...

dihua4417的博客 264

软件可靠性测试与评价

本文系统介绍了软件可靠性工程的核心内容,包括可靠性概念、模型分类、测试方法和评价体系。重点解析了可靠性测试的三大步骤:定义运行剖面、设计测试用例和实施测试收集数据,并以智能安防系统为例说明具体应用。可靠性评价需结合业务场景,通过量化指标(MTBF、失效率等)和模型预测持续优化系统。文章强调可靠性需贯穿软件全生命周期,通过闭环反馈实现持续改进,为关键系统提供质量保障。

moton2017的博客 2050

【自学C++】C++变量作用域

C++ 中的一个变量或常量在程序中都有一定的作用范围,我们称之为作用域。C++ 变量作用域可分为局部作用域和全局作用域。在函数内部声明/定义的变量叫局部变量,局部变量的作用域仅限于函数内部。同时,在函数内部定义的变量,函数的参数和返回值,if 和 for 结构内部使用的变量等都是局部变量。在函数外部声明/定义的变量叫全局变量,C 语言全局变量作用域在整个文件都有效。

haicoder_ibai的博客 790

高级数据结构之R树(R-tree)

R树(R-tree)是一种将B树扩展到多维情况下得到的数据结构,它最初由Antonin Guttman于1984年提出。B树的结点中会存储一个键的集合,这些键把线分成片段,沿着那条线的点仅属于一个片段。因此,B树使得我们可以很容易地找到点。如果把沿线各处的点表示成B树结点,我们就能够确定点所属唯一子结点,在那里可以找到该点。 Antonin Guttman在他提出R树的经典论文中给出的R树例子...

白马负金羁 1万+

基于YOLOv26的卫星遥感环境监测系统

本文介绍了一个基于YOLOv26的卫星遥感环境监测系统。该系统采用分层架构设计,包含数据采集、预处理、目标检测、图像分析、变化检测等核心模块。技术栈包括YOLOv26、PyTorch、OpenCV等深度学习框架和图像处理工具,以及GDAL、rasterio等地理信息处理库。系统支持获取多种卫星数据源(如Sentinel、Landsat、MODIS)的遥感图像,并进行时间序列分析。通过代码示例展示了数据采集模块的实现,包括卫星图像获取和模拟生成功能。该系统可应用于环境要素检测、变化监测等场景,为环境监测提供智

一键难忘的博客 936

C++变量及作用域

C++变量作用域变量定义方式变量类型typedef声明说明变量的作用域全局变量局部变量形式参数静态变量关于全局变量、局部变量、静态全局变量和静态局部变量 变量定义方式 作用:给一段指定的内存空间起名,方便操作这段内存 语法:数据类型 变量名 = 初始值; 变量类型 基本变量类型有int(整型)、char(字符型)等等。 不通过的变量类型占有不同的字节数。 变量类型 字节(位) int 4 unsigned int 4 signed in 4 char 1 unsigned c

Fujintian的博客 2536

前言:关于作者吴秋生博士与此书简介

博士现为田纳西大学,诺克斯维尔分校,地理系的副教授。他的研究兴趣包括地理信息科学(GIS)、遥感和环境建模。更具体地说,对应用地理空间大数据、机器学习和云计算(例如,谷歌地球引擎、微软行星计算机)来研究环境变化,特别是地表水和湿地淹没动态。吴博士是开放科学和可重复研究的倡导者。他开发了几个用于高级地理空间分析的开源软件包(例如,吴博士还在YouTube上发布了数百个有关GIS和地图绘制的视频。更多有关他的开源项目信息,请访问GitHub(查看他的YouTube频道。.....................

韩天放 6091

c++局部变量的内存可以在其作用域之外访问吗

我有以下代码。这段代码运行时没有出现运行时异常!输出结果是58。这怎么可能?局部变量的内存不是在函数外部无法访问吗?

liluo0815481的博客 571

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C++的“持久记忆”:精通 `static` 局部变量

C++函数中的static局部变量具有持久记忆特性,仅在第一次调用时初始化。与普通局部变量相比,其生命周期持续至程序结束,作用域仍限于函数内部。通过计数器示例可见,普通变量每次调用重置为0,而static变量能累积计数,适用于需跨调用保存状态的场景。这种特性结合了全局变量的持久性和局部变量的封装优势。

weixin_42107409的博客 708

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

在函数外存取局部变量的一个比喻

在StackOverflow上一这样一个关于C/C++的问题,问问题的人给了一个代码如下: 12345678910111213int * foo(){    int a = 5;    return &amp;a;} int main(){    int* p = foo();    cout &lt;&lt; *p;    *p = 8;    cout &lt;&lt; *p;} 你可以编译并...

weixin_33961829的博客 100

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C++的“永恒”对象:精通 `static` 对象的生命周期

本文通过酒店房间和银行保险箱的比喻,生动地解释了C++中普通局部变量和static局部变量的关键区别。普通局部变量像"酒店房间",在函数调用时创建、返回时销毁;而static局部变量则像"银行保险箱",只在首次调用时初始化并持续存在至程序结束。文章还对比了static全局变量与普通全局变量的差异,前者具有内部链接性,避免命名冲突。通过Tracer类的实例演示,直观展示了不同类型变量的构造/析构时机,并介绍了static在单例模式中的应用。全文采用生活化类比和代码示例相

weixin_42107409的博客 857

C++的“自动管家”:精通智能指针 (Smart Pointers)

这个挑战让你实践了现代 C++ 中最核心的“工厂”模式:函数。,将“内存所有权”安全地转移给调用者。(共享指针)用于那些**需要“共享所有权”**的复杂场景。”是你(程序员)最重要的职责之一。为了彻底解决这些“人为失误”,现代C++引入了“想把“钥匙”交给别人(比如传递给另一个函数),你。(“自动开关”)离开作用域时,它会自动。现在,你来当一次“内存安全”的工厂厂长。让我们用“X光眼镜”(调试器)来观察。(这在某些罕见的异常情况下不安全)。它所管理的对象(“电灯”)。”(Heap)上分配的内存。

weixin_42107409的博客 758

C++ 内存布局揭秘:栈、堆、静态区与代码区

C++程序运行时的内存区域分为四大类:栈区(Stack)、堆区(Heap)、静态/全局区和代码区。栈区(自动管理)用于存储局部变量和函数调用信息,特点是速度快但空间有限;堆区(手动管理)通过new和delete操作动态内存,生命周期灵活但易引发内存泄漏等问题;静态/全局区存放全局变量和静态变量,其生命周期与程序一致;代码区则存储只读的程序指令。理解这些区域的特点有助于优化内存使用和调试程序。

weixin_42107409的博客 652

C++的“魔法”数组?揭秘VLA(可变长数组)的陷阱

摘要:本文探讨C++中可变长数组(VLA)的使用风险与替代方案。VLA允许运行时确定数组大小(如int vla[n]),但会引发栈溢出风险且违反C++标准。虽然GCC/Clang通过扩展支持VLA,但代码不可移植。文中通过"预制书架"和"现场施工"的比喻,对比了标准数组(编译期固定大小)与VLA的区别,并指出VLA可能导致程序崩溃。推荐使用std::vector作为安全替代方案,它能动态分配堆内存,自动管理资源,且完全符合C++标准。文章包含代码示例,展示VLA的实际

weixin_42107409的博客 1259

C++ 内存管理核心:栈、堆、指针与智能指针详解

C++内存管理核心:栈自动分配释放(快速但有限),堆需手动管理(灵活但易错)。栈适合短生命周期变量,而堆用于需要长期存在的对象。new和delete运算符负责堆内存分配/释放,使用时必须严格配对(new[]对应delete[])。常见内存问题是泄漏(忘记释放)和悬垂指针(释放后仍使用),现代C++通过RAII和智能指针自动管理内存解决这些问题。掌握这些概念是成为合格C++程序员的基础。

weixin_42107409的博客 968

C++指针核心原理:从内存地址到动态内存管理的全面解析

在计算机编程中,内存地址是数据存储和访问的基础概念,它标识了数据在内存中的具体位置。指针作为存储内存地址的变量,其核心原理在于通过地址间接访问和操作数据,实现了对内存的直接控制。这一机制的技术价值在于提供了高效的数据访问方式,避免了不必要的数据复制,从而提升了程序性能。在应用场景上,指针广泛应用于动态内存管理、函数参数传递、构建复杂数据结构(如链表和树)以及实现回调函数等。理解指针与数组的紧密关系、掌握动态内存分配与释放(new/delete),并警惕悬空指针和内存泄漏等常见陷阱,是编写健壮、高效C++程序

715

C++指针深度解析:从内存模型到智能指针的实战指南

指针是计算机编程中用于直接操作内存地址的核心概念,它本质上是一个存储内存地址的变量。理解指针需要从计算机内存模型入手,内存被组织为线性字节序列,每个字节拥有唯一地址,变量则是具名内存区域。指针通过存储地址实现了对数据的间接访问,这一机制带来了动态内存管理、高效函数参数传递、硬件直接访问等关键技术价值,广泛应用于操作系统、数据库、游戏引擎等高性能场景。在C++中,指针语法涉及声明、取地址(&)、解引用(*)等操作,同时需警惕野指针、内存泄漏等常见问题。现代C++通过智能指针(如unique_ptr、share

cuiyingchan0663的博客 395

C++核心概念精讲:类、结构体与指针链表的底层原理与实战

在C++编程中,理解数据结构的底层实现是构建高效、健壮系统的关键。类与结构体作为数据封装的基础单元,通过访问控制实现了数据与行为的绑定,其中深拷贝与浅拷贝的区别直接关系到资源管理的安全。指针作为内存的直接操作工具,是构建动态数据结构(如链表)的基石,它允许程序在运行时灵活分配和连接内存单元。掌握这些概念不仅能帮助开发者手动实现链表等基础数据结构,理解其增删查改的核心操作与边界条件处理,更是深入理解现代C++中智能指针和STL容器(如list、vector)设计思想的前提。本文聚焦类、结构体与指针链表的综合应

weixin_33850015的博客 885

类和对象中-拷贝与赋值

上一章,我们学习了类中默认成员函数中的构造与析构,接下来,我们将继续对剩下的函数进行讲解前景引入:内置类型,比如像int,double这样的类型,比较大小就很简单,就是单纯的比较数值大小。那要是自定义类型呢?就比如我们上面所写的Date类型,其成员变量一共有三个,无法进行简单的数值相比较。所以运算符重载就像是我们自己写一个函数,以我们自己定的规矩来确定哪个变量大,哪个变量小之类的一个方式。本篇知识量比较大,望各位多次学习,多打代码,实践出真知。

Beforeaa的博客 884

组合,聚合,关联,依赖 的区别

组合,聚合,关联,依赖 的区别 依赖与关联 依赖(Dependency ) : 含义:是类与类之间的连接,表示一个类依赖于另外一个类的定义;依赖关系仅仅描述了类与类之间的一种使用与被使用的关系; 体现:在Java和C++中体现为局部变量、方法/函数的参数或者是对静态方法的调用; 举例:人依赖于水和空气;汽车依赖汽油; UML图表示的依赖关系: 依赖: 比如说我要

luhuajcdd的专栏 959
上一篇: 解决android.os.NetworkOnMainThreadException
下一篇: How do I start an Intent from an OnClickListener
勤奋专注
博客等级 码龄19年 80粉丝 69原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值