动态内存管理
存在的意义
我们的内存开辟方式主要是:
int a=0;//开辟4字节的空间
int arr[5]={
0};//开辟20字节的空间
这样开辟存在着几个问题:
由于变量开辟是在内存中的栈中,空间比较有限。
只能开辟固定的内存空间,不方便后续调整。
在特定的场景下我们并不能获取需要开辟的内存大小。
所以我们有动态开辟空间来解决一些特定情景下的问题。
1.不能确定需要的内存大小,或者是一个变化的内存大小
2.可以开辟较大的空间来操作(动态开辟的内存空间在内存堆上,空间大于变量所在的栈空间)
3.不过虽然很好,但是动态开辟的内存空间需要我们申请的人主动释放,否则会造成严重的内存泄漏。
内存泄漏可以说是一个很致命的问题,尤其是在一些连续运行的地方,如服务器代码。严重的会导致服务器瘫痪。
- 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些
存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有
限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。- 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似
于链表。
相关函数
1.malloc
C语言提供了一个动态内存开辟的函数:
void* malloc(size_t size);
//size是指开辟空间的字节数
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
1.开辟成功,则返回一个指向所开辟空间的指针。失败返回NULL;
2.返回值泛型,所以我们可以自己决定。
3.如果参数 size 为0,malloc的行为是标准是未定义的.
我们一般使用malloc的这样用:
int* arr=(int*)malloc(4);
int a[5]={
0};
char* arr1=(char*)malloc(sizeof(int)*5);
//malloc搭配sizeof更方便
//malloc一般使用是带一个强制类型转换
2.free
C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:
void free(void*arr);
要点:
free函数用来释放动态开辟的内存。
1.如果参数 arr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
2.如果参数 arr 是NULL指针,则函数什么事都不做。
int* arr=(int*)malloc(4);
free(arr);
int a[5]={
0};
char* arr1=(char*)malloc(sizeof(int)*5);
free(arr1);
3.calloc
他和malloc一样都是开辟堆内存,只不过他会将开辟的空间全部初始化为0;
他和malloc的形参有不同之处,需要注意。
void* calloc (size_t num, size_t size);
//开辟num个大小为size字节的空间
1.函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
2.与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0
int *arr = calloc(10, sizeof(int));
free(arr);
//不用的时候就一定要free掉内存,虽然我们的代码很短,
//在程序结束自动释放,但是大项目就不一定了。
4.realloc
realloc函数的出现让动态内存管理更加灵活。
他可以改变我们申请的堆空间大小。
void* realloc (void* ptr, size_t size);
//ptr 是要调整的内存地址
//size 调整之后新大小
//返回值为调整之后的内存起始位置。
int *arr = calloc(10, sizeof(int));
int *p=realloc(arr,20);
free(p);
//将calloc出来的40字节变成了20字节
注意:
realloc改变空间大小会出现两种情况:
1.空间起始位置不变(原有空间足够大)
2.起始位置改变
常见错误
1.没有判空
INT_MAX是一个宏定义,代表我们可以向操作系统申请内存空间的最大值,正常情况下无法完成,所以说malloc返回了NULL,而我们对于空指针解引用,属于未定义行为。
void test(){
int *p = (int *)malloc(INT_MAX);
*p = 10;
free(p);
}
2.越界
越界属于未定义行为。不管是动态开辟还是栈空间的变量,都不可以越界访问。
int* arr=(int *)malloc(sizeof(int)*4);
for(int i=0;i<=4;i++){
*(p+i)=1;
}
3.非动态开辟free
free仅支持动态开辟的内存,否则会报错
int arr[5]={
0};
free(arr);
4.未完全释放
未完全释放,造成内存泄漏
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
5.重复释放
这个重复释放内存空间属于未定义行为。
int *arr = calloc(10, sizeof(int));
int *p=realloc(arr,20);
free(p);
free(arr);
6.没有释放空间
动态开辟的内存空间,不用的时候一定要释放。
int* p=(int*)malloc(12);