前言
最开始学习C语言的时候,每每遇到数组我就好奇怎么才能定义一个容量可调节的数组,这个问题直到我学习到动态内存管理与柔性数组的时候才解决。
动态内存管理
动态内存分配
如果预先知道数组的大小就能容易地在栈上定义数组。如果不知道长度,可以定义一个指针,之后调用内存函数,根据需求来动态分配内存。
在此提及一个概念:内存的动态分配是在堆区上进行的,而局部变量的创建和销毁是在栈区上进行的。
<stdlib.h>头文件里提供了这些函数。
malloc
int* p = (int*)malloc(40);
需要强制类型转换
size的单位是字节,指要开辟的空间的大小。
malloc从内存池中提取一块空间,并返回一个指向这块内存的指针。
如果开辟失败,返回值是NULL指针,如果开辟成功。之所以用void类型是因为可以将其强制转换成需要使用的类型。
注:这块内存没有以任何方式进行初始化。
free
free的参数可以是NULL,或是指向用malloc,realloc,calloc开辟的空间的指针。当一块内存不需要再使用时,可以调用free将这块空间归还给内存池,或者说还给操作系统,自此失去这块空间的访问权。
int* p = (int*)malloc(40);
free(p);
p = NULL;
调用free之后指针还是指向起始位置,因此最好把指针置空。
calloc
num表示要存储的元素的个数,size表示要存储的单个元素的字节大小。
返回的同样是指向这块空间的指针。但是有别于malloc,calloc调用后是默认初始化所有元素的值为0的。这个初始化在某种情况下是很方便的,但是有些情况又是冗余的,要根据实际情况使用。
int* p = (int*)calloc(10,sizeof(int));
realloc
这个就有意思了。先来看一段代码:
先用calloc申请一个可以存放10个整型的空间,判断是否开辟成功。如果开辟成功,将这块空间增大到可以存放20个整型的空间,把返回值赋给原来的指针。
这样写是很危险的!
realloc使用有三种情况,
1、当没有空间可以增容时,增容失败,会返回空指针。
所以如果按照图里这样写,一旦开辟失败,arr1会被置空,空间即丢失。
2、当目前这块空间想要增容n个字节,这块空间之后n个字节里的内存没有被使用时,realloc会在这块空间随后的空间里开辟,返回值仍是这块空间的起始地址。
3、当目前这块空间想要增容n个字节,这块空间之后n个字节里的内存已经被使用时, realloc会找到一块足够开辟原空间+n个字节的空间,把原来的空间里的内容转送过来,并且释放原来的旧的空间,返回新空间的地址。
2和3两种情况可以参照上图。
最后,如果realloc的第一个参数时NULL,其功能跟malloc相同。
常见错误
错误释放空间
不是动态开辟的空间不能用free释放,像这种空间是在栈区上开辟的,而不是在堆区,因此不能释放。
试图释放部分内存
这样会导致p偏移到空间中后面的位置,导致释放的时候出现问题,程序会崩溃。 因此如果非要移动指针,要定义一个变量记住指针的起始位置。
重复释放内存
注意:每次使用完后要把P置空,free(NULL)是不会出现问题的,但是如果P释放后没有置空,下次如果不小心再次释放这块内存,程序就会崩溃。 如果空间释放掉之后就不能再访问。
越界访问
对开辟的空间外的内存没有访问权限,会引发访问冲突。
本篇多处知识参考《C和指针》一书。着实是一本好书!
继续加油!ヾ(◍°∇°◍)ノ゙
(本篇完)