一、内存基本构成

可编程内存在基本上分为这样的几大部分:静态存储区、堆区和栈区。他们的功能不同,对他们使用方式也就不同。

静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。

栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。

二、三者之间的区别

我们通过代码段来看看对这样的三部分内存需要怎样的操作和不同,以及应该注意怎样的地方。

例一:静态存储区与栈区

char* p = “Hello World1”;

char a[] = “Hello World2”;

p[2] = ‘A’;

a[2] = ‘A’;

char* p1 = “Hello World1;”

这个程序是有错误的,错误发生在p[2] = ‘A’这行代码处,为什么呢,是变量p和变量数组a都存在于栈区的(任何临时变量都是处于栈区的,包括在main()函数中定义的变量)。但是,数据“Hello World1”和数据“Hello World2”是存储于不同的区域的。

因为数据“Hello World2”存在于数组中,所以,此数据存储于栈区,对它修改是没有任何问题的。因为指针变量p仅仅能够存储某个存储空间的地址,数据“Hello World1”为字符串常量,所以存储在静态存储区。虽然通过p[2]可以访问到静态存储区中的第三个数据单元,即字符‘l’所在的存储的单元。但是因为数据“Hello World1”为字符串常量,不可以改变,所以在程序运行时,会报告内存错误。并且,如果此时对p和p1输出的时候会发现p和p1里面保存的地址是完全相同的。换句话说,在数据区只保留一份相同的数据(见图1-1)。

例二:栈区与堆区

char* f1()

{

char* p = NULL;

char a;

p = &a;

return p;

}

char* f2()

{

char* p = NULL:

p =(char*) new char[4];

return p;

}

这两个函数都是将某个存储空间的地址返回,二者有何区别呢?f1()函数虽然返回的是一个存储空间,但是此空间为临时空间。也就是说,此空间只有短暂的生命周期,它的生命周期在函数f1()调用结束时,也就失去了它的生命价值,即:此空间被释放掉。所以,当调用f1()函数时,如果程序中有下面的语句:

char* p ;

p = f1();

*p = ‘a’;

此时,编译并不会报告错误,但是在程序运行时,会发生异常错误。因为,你对不应该操作的内存(即,已经释放掉的存储空间)进行了操作。但是,相比之下,f2()函数不会有任何问题。因为,new这个命令是在堆中申请存储空间,一旦申请成功,除非你将其delete或者程序终结,这块内存将一直存在。也可以这样理解,堆内存是共享单元,能够被多个函数共同访问。如果你需要有多个数据返回却苦无办法,堆内存将是一个很好的选择。但是一定要避免下面的事情发生:

void f()

{

char * p;

p = (char*)new char[100];

}

这个程序做了一件很无意义并且会带来很大危害的事情。因为,虽然申请了堆内存,p保存了堆内存的首地址。但是,此变量是临时变量,当函数调用结束时p变量消失。也就是说,再也没有变量存储这块堆内存的首地址,我们将永远无法再使用那块堆内存了。但是,这块堆内存却一直标识被你所使用(因为没有到程序结束,你也没有将其delete,所以这块堆内存一直被标识拥有者是当前您的程序),进而其他进程或程序无法使用。我们将这种不道德的“流氓行为”(我们不用,却也不让别人使用)称为内存泄漏。这是我们C++程序员的大忌!!请大家一定要避免这件事情的发生。

总之,对于堆区、栈区和静态存储区它们之间最大的不同在于,栈的生命周期很短暂。但是堆区和静态存储区的生命周期相当于与程序的生命同时存在(如果您不在程序运行中间将堆内存delete的话),我们将这种变量或数据成为全局变量或数据。但是,对于堆区的内存空间使用更加灵活,因为它允许你在不需要它的时候,随时将它释放掉,而静态存储区将一直存在于程序的整个生命周期中。

希望大家记住下面的规则:

【规则1】用malloc 或new 申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL 的内存。
    【规则2】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
    【规则3】避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。
    【规则4】动态内存的申请与释放必须配对,防止内存泄漏。
    【规则5】用free 或delete 释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。

堆与栈的讨论:
管理方式:
  堆中资源由程序员控制(容易产生memory leak)。
  栈资源由编译器自动管理,无需手工控制。

系统响应:
  对于堆,应知道系统有一个记录空闲内存地址的链表,当系统收到程序申请时,遍历该链表,寻找第一个空间大于申请空间的堆结点,删除空闲结点链表中的该结点,并将该结点空间分配给程序(大多数系统会在这块内存空间首地址记录本次分配的大小,这样delete才能正确释放本内存空间,另外系统会将多余的部分重新放入空闲链表中)。
  对于栈,只要栈的剩余空间大于所申请空间,系统为程序提供内存,否则报异常提示栈溢出。

空间大小:
  堆是不连续的内存区域(因为系统是用链表来存储空闲内存地址,自然不是连续的),堆大小受限于计算机系统中有效的虚拟内存(32bit系统理论上是4G),所以堆的空间比较灵活,比较大。
  栈是一块连续的内存区域,大小是操作系统预定好的,windows下栈大小是2M(也有是1M,在编译时确定,VC中可设置)。

碎片问题:
  对于堆,频繁的new/delete会造成大量碎片,使程序效率降低。
  对于栈,它是一个先进后出的队列,进出一一对应,不会产生碎片。

生长方向:
  堆向上,向高地址方向增长。
  栈向下,向低地址方向增长。

分配方式:
  堆都是动态分配(没有静态分配的堆)。
  栈有静态分配和动态分配,静态分配由编译器完成(如局部变量分配),动态分配由alloca函数分配,但栈的动态分配的资源由编译器进行释放,无需程序员实现。

分配效率:
  堆由C/C++函数库提供,机制很复杂。所以堆的效率比栈低很多。
  栈是极其系统提供的数据结构,计算机在底层对栈提供支持,分配专门寄存器存放栈地址,栈操作有专门指令

转载于:https://blog.51cto.com/just2012/1408415

静态存储区、堆和栈的区别相关推荐

  1. C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区

    栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区.里面的变量通常是局部变量.函数参数等.在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用. 和堆一 ...

  2. C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区...

          栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区.里面的变量通常是局部变量.函数参数等.在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调 ...

  3. 栈、堆、静态存储区的三分天下

    1.栈区(stack)-   由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈. 2.堆区(heap) -   一般由程序员分配释放, 若程序员不释放,程序结束 ...

  4. C++堆和栈的区别和联系

    1.堆和栈的含义 在C++中,内存分为5个区:堆.占.自由存储区.全局/静态存储区.常量存储区 1.栈: 由系统自动分配和释放内存,存放函数的参数值,局部变量的值等,分配方式类似于数据结构中的栈 . ...

  5. C++内存管理__内存管理(栈、堆(new/delete)、自由存储区(malloc/freee)、全局/静态存储区、常量区)!堆栈内存管理方式的区别

    内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对C++的痛恨,但内存管理在C++中无处不在,内存泄 ...

  6. C++ 堆、栈、自由存储区、全局静态存储区和常量存储区

    文章来自一个论坛里的回帖,哪个论坛记不得了!    在C++中,内存分成5个区,他们分别是堆.栈.自由存储区.全局/静态存储区和常量存储区.     栈,就是那些由编译器在需要的时候分配,在不需要的时 ...

  7. 堆、栈、自由存储区、全局/静态存储区、常量存储区比较

    1.C语言中分为下面几个存储区 栈(stack): 由编译器自动分配释放 堆(heap): 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 全局区(静态区): 全局变量和静态变量的存储 ...

  8. 【C 语言】内存管理 ( 动态内存分配 | 栈 | 堆 | 静态存储区 | 内存布局 | 野指针 )

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

  9. 栈、堆、静态存储区和程序的内存布局

    文章目录 1 栈 1.1 程序中的栈 1.2 函数调用过程 1.3 函数调用栈上的数据 2 堆 2.1 程序中的堆 2.2 系统对堆的管理方式 3 静态存储区 3.1 程序中的静态存储区 4 程序的内 ...

最新文章

  1. 用java怎么开发图片标注工具_java 图片处理工具 测试
  2. pptv网络电视android,PP视频(原聚力视频)
  3. 华三交换机配置多个镜像口_配置本地端口镜像详解(多个镜像口:多个观察口)...
  4. 计算机应用人才培养策略,新形势下的高校计算机应用人才培养策略
  5. product sales data determination in Opportunity item
  6. [css] ::first-letter有什么应用场景?
  7. centos7 开机启动文件路径_centos7定时运行python脚本
  8. [Java] 蓝桥杯ADV-170 算法提高 数字黑洞
  9. python制作无限弹窗_Python无限弹窗,开机启动,打包为exe程序
  10. C#中索引超出了数组界限如何解决
  11. 百度贴吧诱导用户操作CPA项目
  12. 发光二极管pcb封装图画法_【AD封装】贴片及插件二极管整流桥(带3D)
  13. java对接云之家群聊机器人
  14. Flutter 数据库ORM框架floor使用详解
  15. WINVNC源码分析(四)-vnchooks
  16. HTML——表白树动画
  17. 【成为架构师课程系列】作为一名大数据架构师该掌握的技能清单:
  18. nmn吃第一天有什么感觉,吃完nmn的反应,一点点体会
  19. 如何让在国内访问github网站的速度变快
  20. linux查看摄像头参数+获取公网地址

热门文章

  1. 如何得出数组里最大_相邻两数的最大差值(超巧妙解法)
  2. 程序员吐槽_阿里程序员回老家被哥们吐槽,破IT就破IT,还阿里巴巴
  3. android studio中error,ERROR在Android Studio中
  4. zeromq php,在Apache内部的PHP脚本中使用ZeroMQ
  5. java文件客户端下载_使用Java写一个minio的客户端上传下载文件
  6. .计算机自动关机或重启,电脑自动关机或者重启怎么处理
  7. linux培训机构 网络班,Linux基础教程之网络基础知识与Linux网络配置
  8. 文件夹修改名字 matlab,matlab用 movefile更改文件夹内文件名字
  9. AjaxJson笔记(1)
  10. 2021年程序员1月薪资大幅度上涨,你的2021有奔头了吗?