C++规范编码引涉语法点之(7)静态分配和动态分配/堆和栈

 【1】内存的静态分配和动态分配的区别主要是两个:
       一是时间不同。静态分配发生在程序编译和连接的时候。动态分配则发生在程序调入和执行的时候。
       二是空间不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由函数malloc进行分配。不过栈的动态分配和堆不同,他的动态分配是由编译器进行释放,无需我们手工实现。

【2】对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。
       动态数据区一般就是“堆栈”。“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。

       一般,用static修饰的变量,全局变量位于静态数据区。函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。
        所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。

        例如我们定义一个float型数组:
        float score[100];   
        但是,在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?在很多的情况下,你并不能确定要使用多大的数组,比如上例,你可能并不知道我们要定义的这个数组到底有多大,那么你就要把数组定义得足够大。这样,你的程序在运行时就申请了固定大小的你认为足够大的内存空间。即使你知道你想利用的空间大小,但是如果因为某种特殊原因空间利用的大小有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。这种分配固定大小的内存分配方法称之为静态内存分配。但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。 

       我们用动态内存分配就可以解决上面的问题. 所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。从以上动、静态内存分配比较可以知道动态内存分配相对于静态内存分配的特点: 
1、不需要预先分配存储空间;
2、分配的空间可以根据程序的需要扩大或缩小。 

        要实现根据程序的需要动态分配存储空间,就必须用到malloc函数。malloc函数的原型为:void *malloc (unsigned int size) 其作用是在内存的动态存储区中分配一个长度为size的连续空间。其参数是一个无符号整形数,返回值是一个指向所分配的连续存储域的起始地址的指针。还有一点必须注意的是,当函数未能成功分配存储空间(如内存不足)就会返回一个NULL指针。所以在调用该函数时应该检测返回值是否为NULL并执行相应的操作
       静态内存是在程序一开始运行就会分配内存,直到程序结束了,内存才被释放。
       动态内存是在程序调用在程序中定义的函数时才被分配,函数调用结束了,动态内存就释放。

       由malloc系统函数分配的内存就是从堆上分配内存。从堆上分配的内存一定要自己释放。用free释放,不然就是术语——“内存泄露”(或是“内存漏洞”)—— Memory Leak。于是,系统的可分配内存会随malloc越来越少,直到系统崩溃。还是来看看“栈内存”和“堆内存”的差别吧。

栈内存分配
char* AllocStrFromStack()
{
    char pstr[100];
    return pstr;
}
堆内存分配
char* AllocStrFromHeap( int len )
{
    char *pstr;
    if ( len <= 0 )
        return(NULL);
    return( ( char * ) malloc( len ) );
}

        对于第一个函数,那块pstr的内存在函数返回时就被系统释放了。于是所返回的char*什么也没有。而对于第二个函数,是从堆上分配内存,所以哪怕是程序退出时,也不释放,所以第二个函数的返回的内存没有问题,可以被使用。但一定要调用free释放,不然就是Memory Leak!
       对于malloc和free的操作有以下规则:
1) 配对使用,有一个malloc,就应该有一个free。(C++中对应为new和delete)
2) 尽量在同一层上使用,不要像上面那种,malloc在函数中,而free在函数外。最好在同一调用层上使用这两个函数。
3) malloc分配的内存一定要初始化。free后的指针一定要设置为NULL。

        第一个例中所谓系统释放,应该是指系统在自己的表里把这段内存标记为可以使用,以后可以被别的程序使用,所以第一个例子会造成程序能访问到已经释放的内存空间,是越界,会造成不可预测的情况。
       系统一般不会自动去清除释放空间内的数据,而是由以后的程序来覆盖。所以很多程序开头都会做MEMSET(...) ,就是为了防止这种垃圾数据的情况。
       如果在程序运行中要改变内存块的大小,可以用RALLOC()函数,它能在原来地址上重新分配一块空间。

【3】可以在栈中动态分配内存的函数:alloca()

alloca()函数作用:这个函数的作用是可以从栈里动态分配内存。如果函数A中的某个局部变量在定义时并不知道它有多大,那么就可以将需要使用的空间大小通过函数A的某个参数传递进来,在函数A内部使用alloca分配一块栈内存,alloca函数返回的是这块内存的首指针;不用担心内存泄露问题,因为在函数A返回时,通过alloca申请的内存就会被自动释放掉
优点:
这个函数的优点很明显:
1、有了这个函数,如何定义某个不确定大小的局部变量就变成了简单的问题。其实关于这点,使用malloc函数也可实现,只不过需要程序员自己释放不需要使用的内存,否则会导致内存泄露
2、程序员只需要分配空间,释放空间的事情由alloca函数提供的机制来完成
缺点:
1、如果alloca函数导致栈溢出,程序的行为就undefined了,就是不确定、不可控了,很危险的一个bug
2、可移植性很差,对编译器和平台有很强的依赖性,在很多系统上的实现是有bug的,所以并不鼓励使用。32v、pwb、pwb.2、3bsd、4bsd支持这个函数的实现,Linux使用的是GUN版本,在POSIX和SUSv3系统上是不支持该函数的。需要注意的是很多系统上,alloca函数分配的内存不能被当做某个函数F的参数使用,因为分配到的内存块会被当做参数保存在函数F的栈中(容易导致栈溢出)。
       虽然alloca函数不被推荐使用,但是在Linux系统上,alloca函数却是一个非常好用但没有被人们认识到的工具,它表现的异常出色(在各种架构下,通过alloca分配内存就和增加栈指针一样简单),比malloc的性能要好很多,因为它的申请、释放效率很高。对于Linux下较小内存的分配,alloca能收获让人激动的性能。
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值