动态内存管理

动态内存管理:

(1)变量名名称是一种外在体现,是给成员看的,程序内部指令中都是提高地址进行操作的。

(2)每个函数在调用时都会分配一块栈空间,作为函数的局部存储,函数内定义的局部变量,使用空间都是在函数栈空间中分配的。

一个程序在编译完毕后,哪个局部变量使用什么地址、占用多大内存其实都是确定的(上限8kb)。

一个函数内其实如果定义局部变量并不是无限制定义的,如果定义一个超大变量,超过了函数栈空间大小就会报错。

(3)在一个函数定义一个变量时要注意:

局部变量:栈空间上的内存分配

 1.不能过大,会超出函数栈空间;

2.变量出了作用域空间就会被释放;

3.担忧一些需求:保存一组数据,但是也不知道数据有多少个,因此只能按照上限个数提前定义好数组。但是这样会造成空间的浪费,很多情况下实际用不了那么多。

基于以上局部变量的一些限制,因此提出了另一种内存分配:堆上的内存分配

(4)在堆区分配内存--动态内存分配。

分配的内存特点:

1.手动申请,也必须手动释放(出了申请位置的作用域也不会释放)

(栈上空间的分配--定义了局部变量就会分配空间,出来作用域就会自动释放)

2.需要多少申请多少(按需分配)

3.空间在堆上分配,不占用栈空间(因此不怕函数栈溢出)

PS:申请空间后一定要释放,不释放就会造成内存泄漏。

32位系统上,能支持的最大内存:4G。

代码段和数据段所占用空间大小在程序编译完后时固定的;

栈和堆不同,空间大小不固定。比如函数递归-->栈空间不断分配,动态空间申请-->也是程序运行中需要多少分配多少;

大多数情况下都不要太过考虑申请内存是否会溢出,但也要考虑空间就那么大,因此用时要想好申请多大空间;

一般情况下对都不会溢出,除非内存泄漏,真的内存不够了动态申请空间就会失败。

(5)操作:如何申请空间,如何释放空间

void *malloc(size_t size); //接口

功能:申请size字节大小的空间。

返回值:成功则返回申请到的空间的首地址,失败则返回NULL。

ps:在动态空间申请时一定要进行返回值的判断,因为空间申请有可能会失败。

void free(void *start);  //接口

功能:释放动态申请的空间,参数start是动态申请空间时返回的首地址。

ps:

1)不要对局部变量进行free;

2)空间释放只能从动态申请的首地址释放,不能从中途释放。

malloc申请的空间大小以字节为单位,可以随意申请。但是比如int空间这种,如果申请了3个空间给一个int*变量就会有问题,因为int*指针要访问指向空间的4个字节,但是只申请了3个空间。

动态地址分配,申请的空间是一块连续的空间,因此这块空间可以当作数组来用。

void *calloc(size_t  num, size_t  size);

功能:申请一块空间,并将空间中的数据初始化为0.

参数:num时快个数,size是块大小;num *size就是要申请的空间大小。

返回值:成功返回空间首地址,失败则返回NULL。

ps:malloc与free一定要成对出现;千万不要网忘了释放动态申请的内存

有这么一种需求:原先要保存10个数据因此申请了10个元素空间,但是保存的元素多了,以前的10个空间不够用,因此可以:

1.申请一块新的更大的空间,然后将数据拷贝过来

2.使用realloc重新为原来的空间进行扩容

扩容的前提条件:原先使用的空间后边必须有足够的空闲空间才可以扩容,否则就是重新申请一个新的空间,将数据拷贝过去,返回新的地址。

void *realloc(void *memblock,size_t size);

参数memblock:原来老的空间首地址;       sizeof:要扩容到的的大小

成功则会释放原先的空间,返回新的空间首地址(在原地址扩容成功则不会释放),失败则返回NULL。

扩容的两种情况:(realloc如果传入的原地址空间为NULL,则等价于malloc)

1,原基础扩容:

2.

 free释放一个内存,传入的是空间的首地址。如:int *p=malloc(4) ;   free(p);

p是个指针变量,保存了一块空间地址。free释放的是p指向的空间,而不是p指针变量本身的空间。

(6)在动态内存管理中很容易设计到野指针

野指针:指向了一块不能正常访问的空间(不属于当前的程序)。

若申请了一块内存,将地址赋值给了指针变量p,然后对p进行了free。此时只是把p指向的空间给释放了,并没有修改p变量的指向,意味着此时的p指针就是一个野指针。

PS:对同一块动态内存只能释放一次,且要正确释放。动态开辟内存忘记释放(即内存泄漏)。

动态内存管理中要注意的使用问题

1.不要对局部变量的地址进行释放。

2.动态申请的空间不要从中间释放。

3.释放了指针变量指向的空间后,指针变量会变为野指针,要注意置NULL,不要重复使用(不要继续访问原空间)。

4.动态申请的空间不要重复释放。

5.扩容成功后,有可能原空间会被释放掉,因此一定要保存新的地址。

6.申请与释放一定要承兑出现。

(7)柔性数组:简单理解不是一个数组,是和结构体搭配使用的一种数据结构。结构体最后一个成员是一个没有大小的数组。对结构体空间采用动态内存申请,申请更大(对于结构体大小)的一块空间,数组名就可以将多出来的空间当作数组使用。eg:

定义:在一个结构体中,定义一个没有大小的数组,数组的空间通过动态内存申请。eg:

 给contact申请空间,但是额外多申请了1000个person结构的空间,可以通过data进行访问。

柔型数组注意点:

1.柔型数组前边必须有一个成员,柔型数组要放在结构体的最后边。

2.柔型数组本身不占空间,sizeof(struct contact)大小为4.

3.最后一个没有大小的数组并不占用结构体大小。

4.柔型数组成员必须放在结构体的最后一个位置。

优点:结构体空间的管理较方便。

PS:传入实参的地址,在函数内部通过地址解引用,访问实参的空间。通过这种方式实现,通过形参修改实参。

柔型数组是整体申请空间,整体释放,使用方便。但是当有多个成员变量需要动态申请空间时就不适用。

将成员作为指针,独立进行空间的申请与释放。用时稍微麻烦些,但是更加灵活些。

(8)

局部空间(局部变量)开辟得优缺点

优点:不用管空间得申请与释放,定义变量就会自动开辟空间。

缺点:只能在定义时固定空间大小,无法定义灵活--定义得空间过大较浪费空间少了会不够用。

局部变量的空间都是占用函数栈空间,定义的局部变量过大会导致栈溢出。

出了作用域就会自动释放。

动态内存管理优缺点:

优点:按序申请空间得开辟,空间大小得申请较灵活。不占用函数栈空间因此不用担心开辟空间过大导致栈溢出。申请的空间需要手动释放,出了作用域只要能获取到空间地址就依然可以访问。

缺点:需要手动的申请与释放(尤其是释放时要尤其注意,一不小心就会造成内存泄漏)

操作:void *malloc(size_t size);

void *calloc(int size,int count);

void *realloc(void *ptr,size_t size);

void free(void *ptr);

(9)动态内存管理中的常见错误:

1.针对局部变量空间进行释放。

2.动态申请的空间从中间释放,只能从起始地址处释放。

3.对于一个指针变量,保存了动态申请的空间,free是释放了所指向的空间,但指针变量中依然保留有地址--野指针但是不能继续再去访问被释放的空间--内存访问错误。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值