目录
一.为什么存在动态内存管理
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
所以,我们要养成一个优秀的习惯,
三.常见错误
1.对NULL指针的解引用
有时候申请一块空间,失败后,返回的是空指针.
那假设我们没有对其进行判断是否为NULL,是否申请成功,而直接解引用(使用),那程序自然崩溃.
2.对动态开辟空间的越界访问
申请一块空间,一般指定 sizeof(element)元素的大小*num(元素的个数)
假设我们按字节进行开辟,4个int,我们开辟了16个字节,但循环赋值使用的时候,访问了20个字节,越界访问,则程序一定会崩溃.
3.对非动态开辟的空间free释放
栈区开辟的数组,是不需要free的.
只有使用动态开辟函数,开辟的空间需要free.
4.使用free释放一块动态开辟内存的一部分
5.连续free同一块空间
这里不多说,连续free掉同一片空间,没有任何意义.
6.动态内存空间无释放(内存泄漏)
动态开辟的空间一定要释放,并且正确释放
7.悬空指针
调用free函数,只会释放动态开辟的空间,而不会改变指针的值,指针仍然指向那片空间.
但是free掉后的空间是不能使用的,此时便是悬空指针.
指向动态开辟空间的指针使用完后,一定要记得及时赋值为NULL.
四.在字符串函数中使用动态开辟函数
动态内存分配使得返回指向新字符串的指针的函数称为可能.
所谓新子字符串就是原来不存在的字符串.
我们将其命名为cancat,它的具体功能如图所示:
不同于我们之前提过的strcat函数,concat函数是重新开辟一块空间,将字符串1和字符串2拷贝进
去,而strcat则仍然在原字符串1的空间接字符串2,需要保证dest指向的空间有足够的空间位置,而
concat则不需要考虑.
注意:使用类似concat类似的动态开辟函数,在不使用这块空间时,记得及时free释放掉.
五.经典题目
题目1.
下面程序运行的结果是什么?
如果传指针地址过去,使得str也可以修改为原来p的值,即便p销毁也没关系,所以程序就可以正常运行.
题目2.
下面程序运行的结果是什么?
函数栈帧我们已经讲过相关知识,出了函数后,函数栈帧会被销毁,那其中的数据自然有可能全部重新被随机赋值.那打印不出原来的值,是合情合理的.