动态内存管理

目录

一.为什么存在动态内存管理

二.动态内存管理函数

1.空指针NULL

2.malloc

3.calloc

4.realloc

5.free

三.常见错误

1.对NULL指针的解引用

2.对动态开辟空间的越界访问

3.对非动态开辟的空间free释放

4.使用free释放一块动态开辟内存的一部分

5.连续free同一块空间

6.动态内存空间无释放(内存泄漏)

7.悬空指针

四.在字符串函数中使用动态开辟函数

五.经典题目

题目1.

题目2.


一.为什么存在动态内存管理

C语言的数据结构通常是固定大小的. 

通常我们申请一个数组的空间,它的内存开辟大小是固定的.

 比方说写一个通讯录的程序,一次性开辟能够容纳100个人的空间固然可以,但如果实际上只存储

了2个人,那将会有大量空间被浪费.同理,假如已经存储满100个人,那通讯录则不能再往里面加

入新用户的信息,这也是我们不想看到的.

而C语言为了解决这个问题,支持动态内存管理.即在程序执行期间分配内存单元的能力.

利用动态内存分配,我们就可以把它们链接成表,树或者其它数据结构.

二.动态内存管理函数

1.空指针NULL

当调用内存分配函数,总可能会遇到这样的情况,找不到满足我们需求的足够大的内存空间.

比方说我们申请一块非常大的内存空间,甚至超过堆区总大小,那总不可能返回一个指向该空间的

指针给我们,这时候函数会返回一个空指针NULL.

空指针是不指向任何地方的指针,是一个区别所有有效指针的特殊值,可以把它当成一个垃圾桶,

所有指针初始化,都可以置为NULL.

在申请空间,把函数返回值存储到指针变量之后,一个非常重要的操作就是判断指针是否为空.

因此在分配空间的时候,一定要记得用if语句进行判断是否为空指针.

2.malloc

 

 malloc函数作用是向内存申请一块连续可用的空间,并返回指向该空间的指针.

如果开辟失败,返回一个空指针.

size_t单位是字节,同时是unsigned int类型,不要给malloc一个负数.

如果size为0,会报错,C语言对于该标准是未定义的.

3.calloc

 和malloc函数一样,作用也是向内存申请一块连续可用的空间,并返回一个指向该空间的指针.

但有一个很关键的区别,在于calloc函数可以将申请到的空间初始化.

申请的方式也和calloc函数不太一样,需要给出两个参数.

一个是size_t num-------------元素个数

另一个是size ------------------每个元素的大小,一般配合sizeof使用.

 调试程序可以看到,申请到的空间全部都被初始化为0.

4.realloc

realloc函数用于调整动态开辟的大小,比如有时候我们申请一块空间后,觉得太小,就可以通过realloc函数调整它的大小.

realloc有两个参数

一个是ptr指针,指向以前使用,或分配的内存块的指针 (要调整空间大小,总要告诉电脑是那块内存空间).

当ptr为NULL时,realloc函数等同于malloc函数.

另一个是size,单位是字节,也就是希望重新分配的新空间大小.

当size为0的时候,它会释放掉内存块.

值得注意的是,调整内存空间大小,将其变大,是存在两种情况的

一种是原来需要调整的空间后面有足够位置开辟,那就依旧在原来的地址处接着开辟.

另一种是后方为没有足够空间开辟,那编译器就会寻找一个新的地方,重新开辟我们所需的新空

间,并将原来的数据拷贝在其中.

因此一旦使用realloc更新空间,一定要对指向内存块的所有的指针进行更新,因为它可能改变了空间的位置,使内存块移动到另外的位置.

5.free

函数free,专门是用来做动态内存的释放和回收的.
在使用任何动态分配函数,在不使用那块申请的动态开辟的空间后,都需要free(释放)那块申请
的空间.
不要想着程序结束后,进行自然返回.
这样的累积错误是非常可怕的,不断申请空间,却没有释放,到最后超过可容纳的范围,一旦程序
奔溃,甚至很难发现错误.

所以,我们要养成一个优秀的习惯,

在不使用那块申请的动态开辟的空间后,都需要free(释放)那块申请的空间.

三.常见错误

1.对NULL指针的解引用

有时候申请一块空间,失败后,返回的是空指针.

那假设我们没有对其进行判断是否为NULL是否申请成功,而直接解引用(使用),那程序自然崩溃.

2.对动态开辟空间的越界访问

申请一块空间,一般指定  sizeof(element)元素的大小*num(元素的个数

假设我们按字节进行开辟,4个int,我们开辟了16个字节,但循环赋值使用的时候,访问了20个字节,越界访问,则程序一定会崩溃.

3.对非动态开辟的空间free释放

栈区开辟的数组,是不需要free的.

只有使用动态开辟函数,开辟的空间需要free.

4.使用free释放一块动态开辟内存的一部分

void test ()
{
   int * p = ( int * ) malloc ( 100 );
   p ++ ;
   free ( p ); //p 不再指向动态内存的起始位置
}
看上面这段程序,程序员移动了指向该动态开辟数组的指针,那free掉的只是一部分动态开辟的内
存,并且没有释放那部分空间永远也找不到了.
尽量不要移动 指向该动态开辟数组的指针,移动了也记得及时赋值为NULL.

5.连续free同一块空间

这里不多说,连续free掉同一片空间,没有任何意义.

6.动态内存空间无释放(内存泄漏)

动态开辟的空间一定要释放,并且正确释放

7.悬空指针

调用free函数,只会释放动态开辟的空间,而不会改变指针的值,指针仍然指向那片空间.

但是free掉后的空间是不能使用的,此时便是悬空指针.

指向动态开辟空间的指针使用完后,一定要记得及时赋值为NULL. 

四.在字符串函数中使用动态开辟函数

动态内存分配使得返回指向新字符串的指针的函数称为可能.

所谓新子字符串就是原来不存在的字符串.

我们将其命名为cancat,它的具体功能如图所示:

 不同于我们之前提过的strcat函数,concat函数是重新开辟一块空间,将字符串1和字符串2拷贝进

去,而strcat则仍然在原字符串1的空间接字符串2,需要保证dest指向的空间有足够的空间位置,而

concat则不需要考虑.

注意:使用类似concat类似的动态开辟函数,在不使用这块空间时,记得及时free释放掉. 

五.经典题目

题目1.

下面程序运行的结果是什么?

出函数,形参会被销毁,p被销毁后,动态开辟的空间甚至无法释放.更谈不上p其实没有赋给str,str一直都是NULL.

如果传指针地址过去,使得str也可以修改为原来p的值,即便p销毁也没关系,所以程序就可以正常运行. 

题目2.

下面程序运行的结果是什么?

函数栈帧我们已经讲过相关知识,出了函数后,函数栈帧会被销毁,那其中的数据自然有可能全部重新被随机赋值.那打印不出原来的值,是合情合理的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值