内存分配方式

来源:互联网 发布:淘宝排名靠前技巧 编辑:程序博客网 时间:2024/06/09 21:22

        前几天看到一行关于变量存储位置的代码,感觉很有意思,就拿来分享一下,然后对内存的几种分配类型做一下分析和总结。

        int*p = (int*)malloc(10*sizeof(int));

        变量p是在某个函数体内部定义的局部变量。不难分析到,这里有两个不同的内存块: 变量p和p指向的一块内存。其中,p是一个指针,它本身占据一定的内存空间,该内存空间是由编译器分配,位于栈区;p指向由malloc申请的一块内存区域,该内存区域由程序员自己负责申请,位于堆区。我们可以看到,一条看似简单的语句却包含了两种截然不同的内存分配方式,那么内存的分配类型有哪些呢?该如何判断呢?接下来,我们来进行详细分析。

内存分配类型

         对于C语言而言,内存区域主要分为3个部分:堆、栈和全局/静态存储区。它们的定义和特点如下:

         堆:一般由程序员手动申请和释放,若程序员不释放,程序结束时可能由系统回收。注意它与数据结构中的堆是两回事,分配方式类似于链表。具体地,C语言中使用malloc分配的和C++中使用new分配的内存区域都位于堆区。

         栈:由编译器自动分配和释放,存放函数的参数值、局部变量的值等,甚至函数的调用过程都是用栈来完成。其操作方式类似于数据结构中的栈。

         全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C里面没有这个区分了,他们一起占用同一块内存区。程序结束后由系统释放空间。

下面给出一个具体的例子:

int a = 5;int function(int b ){         int c = 3;         int *p = (int*)malloc(10*sizeof(int));……}

         上例中,变量a是全局变量,位于全局/静态存储区,在编译阶段就分配好内存,并且初始化为5;变量b作为函数的形参,在程序运行时才在栈上分配存储空间;变量c和p都是函数的局部变量,当程序运行到该函数时分配存储空间,位于栈上;变量p指向的是由malloc分配的一块内存,该内存块位于堆上。

         注意,C标准允许使用关键字static来修改变量的存储类型,所以变量定义的位置并不能完全确定变量的存储类型。例如前面的变量c,若我们修改它的定义为static int c,则该变量将存储于内存的全局/静态存储区,在编译阶段就会为其分配内存空间。此外,修改后的变量c在整个程序执行期间有效,但是其作用域仍只限于代码块内部,也就是只能在代码块内部根据名字来访问,在其他地方不能根据名字访问。

堆和栈的主要区别

         位于全局/静态存储区的变量,在编译时分配存储空间,比较容易理解,因此接下来的重点是比较堆和栈:

         管理方式:栈的申请和释放由编译器自动管理,无需程序员手工控制;对于堆来说,由程序员调用相关函数来显式申请,并且释放工作也由程序员控制,这样容易产生内存泄露。

         空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M。当然,这个值可以修改。

         碎片问题:对于堆来讲,频繁的申请和释放势必会造成内存空间的不连续,导致大量的碎片,降低程序的效率。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,不可能有一个内存块从栈中间弹出。

         生长方向:对于堆来讲,从低地址往高地址生长;对于栈来讲,从高地址往低地址生长。

         分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是函数库提供的,它的机制很复杂,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间,就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

         从这里我们可以看到,堆与栈相比,由于大量申请释放操作,容易造成大量的内存碎片;没有专门的系统支持,效率很低。虽然栈有很多优势,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,用堆可能会好一些。

         总之,堆栈各有各的优势和不足,我们使用的时候根据具体情况加以选择。

0 0
原创粉丝点击