动态内存函数
# malloc函数
如上图,malloc函数被用来申请10个整形大小的空间,malloc函数的返回类型是void*,因为malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来定义。因此我们需要对他进行强转,然后赋给p即可使用。
malloc函数的返回值: 如果申请的空间开辟成功,则返回一个指向开辟空间的指针。
如果开辟失败,则放回NULL指针,就不能使用了。因此我们需要对返回 值进行检查。
如果参数为0,malloc行为标准未定义,取决于编译器。
我们会习惯性的对malloc的返回值进行检查,如上图,如果返回了NULL,则打印错误的信息。
# free函数
free函数是专门用来做动态内存的释放和回收的。
malloc开辟的空间有两个释放方式:
1.free释放——主动
2.程序退出后,malloc申请的空间,也会被系统回收——被动
正常情况下,我们要主动释放。
free函数没有返回值,他的参数就是开辟空间的地址。
如上图,我们释放时,只是释放了开辟的空间,但p还是指向那个地址,因此,我们会在free之后将该指针变为空指针,否则该指针就是野指针,野指针是危险的。
- 如果free参数指向的空间不是动态开辟的,则free函数的行为是未定义的。
- 如果参数是空指针时,则函数什么也不做。
malloc和free都需要引用头文件stdlib.h。# calloc函数
calloc函数和malloc函数返回值类型都一样void*,calloc的参数有两个,参数1指开辟空间的个数,参数2指开辟空间的类型的大小。
如上图,同样开辟10个整形大小的空间,二者的差别不大。除了参数的区别,calloc函数申请好空间后,会将空间初始化为0,但是malloc不初始化。# realloc函数
- realloc函数有两个参数,参数1是要调整的空间的地址。
- 参数2是调整之后的大小。
- 返回值是调整之后的内存起始地址。
- 这个函数调整原内存空间的基础上,还会将原来内存中的数据移到新的空间。
realloc在调整内存空间存在两种情况:
假设初始已申请10个整形的空间,现需要调整为20个整形的空间。
- 原有空间之后有足够的空间
- 原有空间之后没有足够大的空间
如上图,因为后面没有足够大的空间,realloc函数会找一块新的,足够的空间,一次性开辟需要的空间。 - 旧的空间中的数据,会拷贝到新的空间中。
- 释放掉旧的空间。
- realloc函数返回新的空间的地址。
realloc也能做malloc能做的事,如果参数1是空指针,上方realloc函数的作用跟注释中malloc函数的作用一样。几道经典笔试题
# 题1
分析:str指向的空间仍为NULL,因为GetMemory后p会被销毁,程序对str(NULL)进行解引用操作,会使程序崩溃。但是malloc开辟的空间依旧存在,没有释放,会造成内存泄漏。
修改后的代码如下:
# 题2
分析:p的地址返回给str,但返回时,该空间已经销毁了,即没有了该空间的使用权,str指向了p所指向的地址,但此时str是野指针。
# 题3
分析:缺少free,造成内存泄露。
# 题4
分析:free后,str指向的空间被释放了,但他依旧指向该地址。strcpy时,此时str为野指针,对野指针进行操作,非法访问内存。
柔性数组
结构体中最后一个元素允许是未知大小的数组,这就叫柔性数组成员。
# 柔性数组的特点
- 结构体中的柔性数组成员前面必须至少有一个其他成员
- sizeof返回这种结构大小不包括柔性数组的内存
- 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
如上图,不包括柔性数组的内存。# 柔性数组的优点
请看下面两组代码# # 组1:
# # 组2:
分析组2:组2的结构体中有柔性数组成员,先是申请一块空间,后来空间不够,就realloc进行调整,并把调整后的地址传给先前的ps,开辟的空间是连续的。在释放时,只需要释放一次。
分析组1:组1先是malloc一块空间,然后在data中再malloc一块空间。空间不足时,再realloc调整,然后把新空间的地址传给data。malloc开辟的空间不是连续的。由于malloc了两次,在释放时也需要释放两次,即malloc几次,就要free几次,这样释放内存相较于组1,更为麻烦。如下图。
组2有两个优点:
1.方便内存释放:
2.有利于访问速度