Chinaunix首页 | 论坛 | 博客
  • 博客访问: 231162
  • 博文数量: 59
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 493
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-26 17:46
个人简介

做技术要:鹰击长空,鱼翔浅底。

文章分类

全部博文(59)

文章存档

2017年(1)

2016年(3)

2015年(27)

2014年(28)

分类: 嵌入式

2015-01-26 16:23:21

C语言中内存分为三个部分:静态区,栈,堆。

堆的英文是heap;栈的英文是stack,我们常说的堆栈其实是栈;
堆和栈都有自己的特性,这里先不做讨论。
堆是程序员分配和回收的
栈是C语言本身分配回收
静态区:保存自动全局变量和static 变量(包括static 全局和局部变量)。
静态区的内容在总个程序的生命周期内都存在,由编译器在编译的时候分配。

栈:保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,
这些内容也会自动被销毁。其特点是效率高,但空间大小有限。
堆:由malloc 系列函数或new 操作符分配的内存。其生命周期由free 或delete 决定。
 一、内存基本构成
    
    可编程内存在基本上分为这样的几大部分:静态存储区、堆区和栈区。他们的功能不同,对他们使用方式也就不同。
    
    静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量(包括字符串常量)。
    
    栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。由操作系统控制。
    
    堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。
    
    二、三者之间的区别
    
    我们通过代码段来看看对这样的三部分内存需要怎样的操作和不同,以及应该注意怎样的地方。
    
    例一:静态存储区与栈区
    
    Void main()
    
    {
    
    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里面保存的地址是完全相同的。换句话说,在数据区只保留一份相同的数据(这里面我理解的意思(在网上有其他人这样说),定义字符串数组,是将字符串进行了一次copy,也就是将Hello World2拷贝到了a[],也就是说a指向的内容是"Hello world", 但是这是复制品,不是原件)(见图1-1)。
    
    例二:栈区与堆区
    
    char* f1()
    
    {
    
    Char p[] = "hello";//栈区
    
    return p;
    
    }
    
    上述代码gcc编译会报错:warning: function returns address of local variable
    
    结果:st = p;打印出乱码
    
    char* f2()
    
    {
    
    char* p = NULL:空指针,表示指针ipAddr暂时没有存放地址,只是开辟了一个内存,但内存没存放地址。也就没用指向,当有地址存放进去后就有了指向//在堆区
    
    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的话),我们将这种变量或数据成为全局变量或数据。但是,对于堆区的内存空间使用更加灵活,因为它允许你在不需要它的时候,随时将它释放掉,而静态存储区将一直存在于程序的整个生命周期中。
    
    我们此专题仅仅是简要的分析了内存基本构成以及使用它们时需要注意的问题。对内存的分析和讨论将一直贯穿于我们以后所有的专题,这也就是为什么把它作为第一讲的原因
阅读(2918) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~