一、动态内存分配
在之前的操作中,我们都是在栈空间上开辟内存空间
如:
int key = 10;
char arr[10] = "abcdef";
但是这种开辟空间的方式存在几个问题;
空间一旦开辟其大小是确定的
对于数组来说,在创建的时候要给出数组长度,一旦确定数组长度无法调整
而在实际问题中,我们需要的空间大小有可能在程序运行时才能清楚,不能提前指定空间,那么原先这种开辟内存的方式就显得不够灵活。
因此,我们有了动态内存分配,旨在让我们自己可以灵活地申请和释放空间,其申请的空间在内存中的堆区
二、malloc
malloc是C语言提供的一个开辟内存的函数:
void* malloc (size_t size);
这个函数会申请一块连续可用的空间,并且返回指向这块空间的指针。
若开辟成功,返回指向该空间的指针,若开辟失败,返回NULL指针,因此要对其返回值进行判断
参数的返回类型是void*,可以任意,具体由我们自己决定
若传入的参数size是0 malloc函数的行为未定义,取决于编译器
具体我们来看怎么使用:
要注意,动态内存分配函数申请的大小都是以字节为单位的
在这里,我们看了开辟整型空间的效果,20个字节即5个整型,然后就可以对开辟的空间进行赋值打印等操作
当然,我们上面说了要判断返回的是否为空指针,使代码更安全
那么我们再来看一个结构体的例子:
如图,以上我们就成功为结构体开辟了空间,接下来就可以使用
三、free
free函数,是用来做动态内存的释放和回收的
上面我们讲了用malloc来开辟动态内存,那么当我们使用完后,就需要释放该空间还给操作系统,如果不释放,那么这块空间会一直存在,程序会吃掉内存,大大影响系统性能 。
void free (void* ptr);
对于free函数,其接受类型为指针,该指针指向的是动态内存开辟出来的空间。
如果ptr指向空间不是动态开辟的,则free函数行为未定义
若ptr指向NULL指针,则free函数什么都不做
在我们上面的例子中,在使用完这块空间之后,是需要释放的 于是我们free(p) 然后将p置为空指针,那么将p置为空指针是否有意义呢?
在我们释放该空间之后,这块空间的使用权限还给操作系统,但是p依然指向这块空间,只不过不能再访问这次空间了,等下次操作系统重新分配空间,p很可能会成为野指针,在程序运行中很危险,因此我们有必要在释放后将p置为NULL
在我们刚刚创建的结构体中free和置空指针也是必须的,但是我们刚刚用malloc开辟了两次空间,那么应该先释放哪一个呢,顺序也很有考究。
如果我们先释放p,那么p指向的这个结构体的空间就不能被访问了,我们也就不能通过p访问结构体成员int*arr 然后释放arr 那么这时候p->arr就不能被释放,会给程序带来隐患,因此我们必须先释放p->arr 再释放p 记得还要都置为空指针
四、calloc
calloc也是用来动态内存分配的,其与malloc有一些区别
void* calloc (size_t num, size_t size);
参数num是元素个数,size是每个元素的大小,单位是字节
calloc函数会开辟num个size大小的元素空间,并且把空间的每个字节均初始化为0
用法与malloc几乎相同,只需要记得calloc会把字节全部初始化为0
五、realloc
尽管我们已经可以自己申请空间了,但有时候申请空间很大,有时候申请空间很小,我们应该怎么灵活调整呢 这时候我们就用到realloc函数调整动态开辟的内存的大小
void* realloc (void* ptr, size_t size);
ptr是要调整的内存地址,size是调整之后的新的大小 返回值是调整之后的内存的起始位置
注意:realloc函数在调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
realloc在调整空间的时候存在两种情况:
1、原有空间之后有足够大的空间
当时这种情况的时候,要扩展内存就会直接在原内存之后继续追加空间,原来空间的数据不会变化
2、原有空间之后无足够大的空间
这种情况下,会找另一个合适大小的空间来使用,于是函数返回一个新的内存地址
如图,我们第一时间写出这样的代码,将20个字节的大小扩展到100个字节大小,并将返回的地址给p接收
但是,如果realloc扩容时失败了呢,会返回空指针,有风险,所以我们先判断返回地址
本节我简单介绍了动态内存分配的概念以及其中常用的几个函数,但是仅仅这些还不够,下节中我会为大家继续介绍动态内存分配中常见的一些错误等等