为什么要有动态内存分配?
通过之前的学习,我们已经掌握的内存开辟方式有:
int a = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
上述空间的开辟的大小是固定的
数组在申明的时候,必须指定数组的长度,数组空间一旦确定了大小不能进行调整。
但是c语言引入了动态内存开辟,可以自己申请和释放空间,比较灵活。
malloc和free
malloc
void* mallloc (size_t size);
是c语言提供的一个开辟动态内存的函数。
返回值是void*,所以并不知开辟空间的类型,具体在使用的时候自己做决定。
这个函数可以向内存申请连续可用的空间,并返回指向这块空间的指针。
例如:申请10个整型的空间
#include<stdio.h>
int main()
{//申请10个整型的空间 int* p = (int*)malloc(10*sizeof(int));return 0;}
但是空间的开辟不一定都是成功的,所以要进行判断,如果开辟成功就返回起始地址。
如果开辟失败,就返回一个NULL指针,所以对返回值一定要做好检查。
#include<stdio.h>
int main()
{//申请10个整型的空间 int* p = (int*)malloc(10*sizeof(int));//对内存的申请是否成功,进行判断。 if(p ==NULL){//空间开辟失败perror("malloc");return 1; } return 0;}
free
c语言提供了一个函数free,专门是用来做动态内存的释放和回收的。
void free(void* ptr);
free函数用来释放动态开辟的内存。
如果参数ptr 指向的空间不是动态开辟的,那么free函数的行为是未定义的。
如果参数ptr是NULL指针,则函数什么事都不用做。
malloc和free都声明在 stdlib.h 头文件中。
例如:
#include<stdio.h>
#include<stdlib.h>
int main()
{int* ptr=NULL;int num=0;scanf("%d",&num);ptr = (int*)malloc(num*sizeof(int));//对内存的申请是否成功,进行判断。 if(*ptr ==NULL){//空间开辟失败perror("malloc");return 1; } int i=0;for(i=0;i<num;i++){*(ptr+i)=0;} free(ptr);//释放ptr所指向的动态内存。ptr = NULL; return 0;}
一旦free之后,p指完的空间,不属于当前程序,但是可以找到。
为了防止ptr成为野指针,所以最后加一个ptr = NULL
局部数组不可以释放,malloc和free最好成对使用。
calloc和realloc
calloc
c语言还提供了一个函数叫calloc,calloc函数也用来动态内存分配。
void* calloc (size_t num,size_t size);
函数的功能为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0.
与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每一个字节初始化为全0.
#include<stdio.h>
#include<stdlib.h>
int main()
{//申请10个整形的空间//malloc(10*sizeof(int));int *p = (int*)calloc(10,sizeof(int));if(p==NULL){perror("calloc");return 1;} //使用空间int i=0;for(i=0;i<10;i++){printf("%d ",p[i]);//*(p+i)} //释放free(p);p = NULL; return 0;
}
如果我们对内存空间的内容要求初始化,那么可以很方便的使用calloc函数来实现。
realloc
realloc函数的出现是让动态内存管理更加灵活。
有时我们发现过去申请 的空间太小了,或者太大了,为了合理的使用内存,我们一定会对内存的大小做灵活的调整,realloc函数就可以对动态开辟内存的大小进行调整。
void* realloc(void* ptr,size_t size);
ptr是调整的内存地址,
size 调整之后的大小。
返回值为调整之后的内存的起始地址 。
这个函数调整内存空间的大小的基础上,还会将原来内存中的数据移动到新的空间。
分为两种情况
int main()
{int* ptr = (int*)malloc(20);//.....if(ptr != NULL){int* tmp = realloc(ptr,40);//...}return 0;
}
1,原有的空间由足够大的空间。
2,原有的空间之后没有足够的空间。
(1)对于第二种:如果后续的空间不够,realloc函数直接在内存的堆区找一块新的满足大小的空间
(2)将旧的数据,拷贝到新的空间
(3)释放新的空间
(4)返回新的地址
#include<stdio.h>
int main()
{//申请10个整型的空间 int* ptr = (int*)malloc(10*sizeof(int));//对内存的申请是否成功,进行判断。 if(ptr != NULL){//业务处理 } else {return 1;} //扩展容量//代码1--直接将realloc的返回值放到ptr中ptr = (int*)realloc(ptr,1000);//这样的方式正确吗?//代码2--先将realloc函数的返回值放在p中,不为NULL,再放在ptr中int* p = NULL;p = realloc(ptr,1000);if(p != NULL){ptr = p;} //业务处理free(ptr); return 0;}