为什么存在动态内存函数?
我们已经掌握的开辟方式有int a,char b,float arr[10]
我们会发现,它的内存大小是固定不变的,也就是随着存储的数据越来越多,迟早有一天他会超过限制,造成越界访问。
因此,我们需要一种可以随着我们需求而变化的开辟空间的方法——动态内存开辟
malloc
malloc是c语言自带的一个内存开辟函数,我们先来看看它的函数类型
- 这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回一个指向开辟好空间的指针。
- 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
- 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
- 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。,
free
free函数是专门用来释放掉动态内存的开辟的空间的,也是一个函数。
我们只需要把要释放的空间的首地址传给它即可,它就会将这段动态开辟的空间释放,还给操作系统
注意:
- 如果我们在函数内部进行动态开辟,但是没有进行释放的话,它是不会和int,char类型一样被销毁的,而是会一直存在,直到程序结束才返回给操作系统
free
函数用来释放动态开辟的内存。- 如果参数
ptr
指向的空间不是动态开辟的,那free
函数的行为是未定义的。 - 如果参数
ptr
是NULL
指针,则函数什么事都不做。 - 任何只要是开辟的动态内存空间(堆上的),都要
free
释放返还给操作系统。 - free不会把接收的指针自动置空,需要自己手动操作
calloc
calloc和malloc类似,也是用来进行动态开辟内存的函数,不过其与malloc还是有一些区别所在
malloc和calloc的异同
异:
1.函数参数和malloc不同,size是一个元素的大小,而num则是元素的个数,两个相乘才是malloc里的size。
2.calloc会将默认的元素初始化为0,而malloc则是随机值
同:
都会进行动态内存的开辟
realloc
我们都将动态内存,那么这一块空间应该就是动态的。我们malloc出来的空间也是有限的,那么当他不够用的时候,就轮到realloc函数出马了,我们先来看它的声明
memblock是经过动态内存开辟后仍需拓展的空间的首地址
size是经过拓展后该空间的总大小
返回经过拓展后这一空间的首地址,类型是void*
realloc是在堆区上继续开辟空间,但是它有两种方式,理由如下:
我们知道在堆区上存储着许多数据,动态内存开辟的空间是连续的,在堆区的位置也是相对随机的,所以在需要调整的空间后面继续追加空间的话,有可能会碰到已经存储着数据的空间,但是我们又不可能破坏这些数据,所以realloc就有两种方式进行动态内存开辟
1.在原空间后面直接追加空间
2.在堆区里另外寻找一块大小为拓展后的空间,将原来的空间拷贝过来后,将原来的空间还给操作系统,再返回这块新的大小为拓展后内存大小的空间的地址。但是如果找不到额外的空间的话,realloc就会返回NULL
其实realloc也可以和malloc一样开辟空间
int main()
{
int* p1 = NULL;
int* p2 = realloc(p1, sizeof(int) * 10);
return 0;
}
常见动态内存使用错误
1.对NULL空指针的解引用操作
这其实也是我们平时会遇到的问题,和void*类型一样,我们不能对NULL进行加减,解引用等操作,否则系统就会报错
2.对动态内存空间进行非法访问
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
3.忘记free导致内存泄漏
int main()
{
while(1)
{
int*p=malloc(10);//不断向堆区索要空间,最终堆的空间会枯竭
}
return 0;
}
4.只free掉一部分动态内存开辟的空间
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
5.对同一块空间多次free
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
6.对非动态内存空间进行free
void test()
{
int a = 10;
int *p = &a;
free(p);
}
总结:
在使用动态内存中,我们需要额外注意内存泄漏以及以上常见错误,不然程序跑起来可能会有很多bug
希望本篇博客对大家有所帮助,希望大家多多支持,我也会继续努力滴。