静态数据区 堆区 栈区的区别

一、内存基本构成 可编程内存在基本上分为这样的几大部分静态存储区、堆区和栈区。他们的功能不同对他们使用方式也就不同。 静态存储区内存在程序编译的时候就已经分配好这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。 栈区在执行函数时函数内局部变量的存储单元都可以在栈上创建函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中效率很高但是分配的内存容量有限。 堆区亦称动态内存分配。程序在运行的时候用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里面保存的地址是完全相同的。换句话说在数据区只保留一份相同的数据
例二 栈区与堆区 
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++函数库提供机制很复杂。所以堆的效率比栈低很多。    
栈是极其系统提供的数据结构计算机在底层对栈提供支持分配专门寄存器存放栈地址栈操作有专门指令。

1. int *p = &a; 这样的指针不可用 free  free 只是针对 malloc realloc 的内存进行释放。 
2. 空指针是指指针指向地址0如果是你分配的内存并且使用 free 释放然后把指针置成 0 只是为了不进行非法的引用。 如
 char* tmp = (char*)malloc(102);
free(tmp); 
tmp = 0;
  在 free 后如果后面再引用 tmp, 因为是个空指针会导致进行崩溃。
 如果不置为0,比如 
char* tmp = (char*)malloc(1024);
 free(tmp);
 char* tmp2 = (char*)malloc(1024);
 memset(tmp, 0, 1024);
 这样由于堆管理特性很可能 memset(tmp, 0, 1024)写了 tmp2 指向的空间。 这样释放完 tmp 后再使用它谓之野指针。 使用野指针会造成不可预期的后果而使用空指针会造成比较确定的后果崩溃。 所以 释放完后给指针赋成空很大程度是避免以后错误地使用指针。 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值