动态内存分配

  • 指针可以存放其它变量的地址,此外还有一种用法,就是保存一系列内存位置所在地址,这块内存,在编译期并不对应某个变量的变量名,而是等到程序运行起来后,再有系统做动态分配,这样的内存有时被称为堆(Heap)。

  • 静态内存分配:使用的是栈(Stack)空间内存,只需在编程的时候直接声明即可。缺陷:很多时候,会浪费大量内存空间,在少数情况下,当定义的数组不够大时,可能引起下标越界错误。

  • 动态内存分配特点:

[1].不需要预先分配内存空间。

[2].分配的控件可根据程序需要扩大/缩小。

[3].动态内存分配的生存期由我们自己决定。注意:如果没有释放的话,很容易造成内存溢出,因为heap中的内存块是全局的,不会随函数的调用而结束。


  • 动态内存分配相关函数:

C语言:

[1]malloc函数:

头文件:malloc.h / stdlib.h

void * __cdecl malloc(_In_ size_t _Size);

功能:允许从空闲内存池中分配连续内存但不初始化

参数:typedef _W64 unsigned int   size_t;

返回:分配成功,返回指向该内存块的指针,在使用时可根据需要进行强转,否则返回NULL,在使用时需要判空

例:int* Ptr = (int* )malloc(n*sizeof(int)); //在空闲内存池中分配连续内存n*sizeof(int)个字节的堆内存空间

注:__cdecl :C Declaration,表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。

_In_宏,告诉我们,该变量或参数是输入值,即必须给这个变量填写好以后提交给某个函数去执行。

[2]calloc函数:

头文件:malloc.h / stdlib.h

void * __cdecl calloc(_In_ size_t _Count, _In_ size_t _Size);

功能:允许从空闲内存池中分配连续内存并初始化为0.

参数:_Count是所需元素的数量, Size每个元素的字节数

返回:分配成功,返回指向该内存块的指针,在使用时可根据需要进行强转,否则返回NULL,在使用时需要判空

例:int *CPtr = (int *)calloc(10, sizeof(int));

[3]realloc 函数

头文件:malloc.h / stdlib.h

void * __cdecl realloc(_Post_ptr_invalid_ void * _Memory, _In_ size_t _NewSize);

功能:在指针_Memory指向的内存基础上扩大或缩小内存(更改已经配置的内存空间),但不初始化。

参数:_Memory是先通过malloc,calloc,realloc函数分配内存块后的指针,NewSIze是内存块的新尺寸。

返回:如果调用成功,不管当前内存段后面的空闲空间是否满足要求,都会释放掉原来的指针,重新返回一个指针,虽然返回的指针有可能和原来的指针一样,即不能再次释放掉原来的指针。如果申请失败,则返回NULL,此时,原来的指针仍然有效。

注:

①如果当前的内存段后面有需要的内存空间,则直接扩展这段内存空间,realloc()将返回原指针。

②如果当前内存段后面的空闲字节不够,则使用堆中第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置。

③如果申请失败,则返回NULL,此时,原来的指针仍然有效。

④若第一个参数为NULL,那么功能等同于malloc函数,若第二个参数为0,那么会释放调用的内存块。例:

    realloc(NULL,10*sizeof(int)); //等同malloc(10*sizeof(int));

    realloc(Ptr,0); //等同于free

 

示例:

#include <stdlib.h>
#include <string.h>

void _tmain(int argc, _TCHAR* argv[])
{
     unsigned int n = 10;
     int* Ptr = (int* )malloc(n * sizeof(int)); //在空闲内存池中分配连续内存n*sizeof(int)个字节的堆内存空间
     if (Ptr != NULL)
     {
         memset(Ptr, 10, n*sizeof(int));
     }
     else
     {
         exit(1);
     }

     //重新分配内存空间,如果分配成功的话,就释放Ptr指针,
     //但是并没有将Ptr指针赋为NULL,也就是说释放掉的是系统分配的堆空间,
     //和该指针没有直接的关系,现在仍然可以用Ptr来访问这部分堆空间,只是
     //现在的堆空间已经不属于该进程的了
    int *CPtr = (int *)realloc(Ptr, (n+5) * sizeof(int));
    if (NULL == CPtr)
    {
        exit(1);
    }
    //初始化,一个字节一个字节地将整个数组设置为一个指定的值。
    memset(CPtr, NULL, n*sizeof(int));

    //只需释放CPtr,在调用realloc时,Ptr已经释放掉了。
    free(CPtr);

    getchar();
}

[4]free函数:

void   __cdecl free(_Post_ptr_invalid_ void * _Memory);

功能:释放内存空间,将内存释放出来给系统。

说明:

①free与malloc是成对出现的。

②malloc申请时,尽量进行初始化,防止后面出现的不确定性的东西。

③malloc的生命周期,只要没有调用free,进程没有结束,这个函数的生命周期就会一直存在在内存中。

④free之后如果还有这块内存地址的话,此时该内存内存归还给了系统(可能这块内存还处于一个空闲状态),但是还可以对其进行操作,里面的值会短暂的保留。

free之后,申请内存的那个指针就会变成野指针(声明了,但是没有任何指向的野指针),有时候会出现野指针错误;所以尽量在操作之后,将指针置为NULL。

          ⑤申请和释放是成对的,所以程序是不能进行多次free的,否则会崩溃。

C++语言:

New-delete是C++的关键字,同时也是操作符

[1] new用法

①new的过程:

当我们使用关键字new在堆上动态创建一个对象时,它实际上做了三件事:获得一块内存空间、调用构造函数、返回正确的指针。如果我们创建的是简单类型的变量,那么第二步会被省略。

开辟单变量地址空间:

使用new运算符时必须已知数据类型,new运算符会向系统堆区申请足够的存储空间,如果申请成功,就返回该内存块的首地址,如果申请不成功,则返回零值。

一般使用格式:

格式1:指针变量名=new 类型标识符;

格式2:指针变量名=new 类型标识符(初始值);

格式3:指针变量名=new 类型标识符 [内存单元个数]

说明:格式1和格式2都是申请分配某一数据类型所占字节数的内存空间;但是格式2在内存分配成功后,同时将一初值存放到该内存单元中;而格式3可同时分配若干个内存单元,相当于形成一个动态数组。

开辟数组空间

对于数组进行动态分配的格式为:

       指针变量名=new 类型名[下标表达式];
                  delete [ ]
指向该数组的指针变量名;

 两式中的方括号是非常重要的,两者必须配对使用,如果delete语句中少了方括号,因编译器认为该指针是指向数组第一个元素的指针,会产生回收不彻底的问题(只回收了第一个元素所占空间),加了方括号后就转化为指向数组的指针,回收整个数组。

delete []的方括号中不需要填数组元素数,系统自知。即使写了,编译器也忽略。

请注意下标表达式不必是常量表达式,即它的值不必在编译时确定,可以在运行时确定。

    一维: int *a = new int[100];    //开辟一个大小为100的整型数组空间

    二维: int **a = new int[5][6]

    三维及其以上:依此类推.

    一般用法: new 类型 (初值)

[2]delete用法

删除单变量地址空间

    int *a = new int;

    delete a;   //释放单个int的空间

②删除数组空间

    int *a = new int[5];

    delete []a;    //释放int数组空间

 

[3]使用注意事项:

new delete都是内建的操作符,语言本身所固定了,无法重新定制。

动态分配失败,则返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。

指针删除与堆空间释放。删除一个指针pdelete p;)实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身(指针p本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放),释放堆空间后,p成了空指针。

内存泄漏(memory leak)和重复释放。newdelete 是配对使用的, delete只能释放堆空间。如果new返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,所以必须妥善保存new返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。

动态分配的变量或对象的生命期。我们也称堆空间为自由空间(free store),但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放,往往会出错。

要访问new所开辟的结构体空间,无法直接通过变量名进行,只能通过赋值的指针进行访问。

用delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。

[4]常见的内存错误:

(1)使用未分配成功的内存
           避免方式:在使用内存之前检查指针是否为NULL;

(2)引用分配成功但尚未初始化的内存
           避免方式:赋予初值,即便是赋予零值也不可省略

(3)内存分配成功并且已经初始化,但操作越过了内存的边界
           避免:注意下表的使用不能超出边界

(4)忘记释放内存,造成内存泄露
           避免方式:申请内存的方式和释放内存的方式需要成双成对

(5)释放内存之后却继续去使用这一块内存
           避免方式:使用free内存之后,把指针置为NULL;

内存错误的注意点:

指针消亡了,并不表示它所指向的内存会被自动释放,(在free之前,直接将指针设为NULL); 
           内存释放了,并不代表指针会消亡或者成了NULL指针;(在free之后,指针并没有进行NULL设置);

野指针:

①野指针的形成是指针变量没有被初始化或指向已删除的内存,任何指针变量刚被创建的时候不会自动成为NULL指针,它的缺省值是随机的,会乱指一气。

②指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法内存。

③ free内存块之后,需要将指针设置为NULL,如果没有设置为NULL,也会出现“野指针”,它是指向“垃圾”内存的指针。多次free内存块,是会导致程序崩溃的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值