动态内存管理
1.为什么要有动态内存分配
int n=10;//4个字节
char c;//1个字节
char arr[20];//20个字节
int arr[20];//80个字节
上面这申请内存空间的方式
一旦申请好,大小就无法调整
1.2动态内存的四个函数
1.malloc——向内存申请一块连续可用的空间,并返回指向这块空间的指针(返回起始地址)
void*malloc(size_t size)
//size 的单位是字节
如果开辟成功,返回开辟好空间的指针(就是起始地址)
如果开辟失败,返回NULL指针,因此malloc的返回值要检查好
void*,不知道要开辟什么类型的空间,由使用者自己决定
#include<stdlib.h>
int main()
{
int*p=(int*)malloc(20);//使用时自己决定类型
if(p==NULL)//判断开辟成功没有
{
perror("malloc");//空指针,开辟失败,报错
return 1;
}
int i=0;
for(i=0;i<5;i++)
{
*(p+i)=i+1;
}
return 0;
}
注意:要包含stdlib.h文件
2.free——专门用来做动态内存的释放和回收
void free(void* ptr);//参数是个指针变量,给他传个地址过去
//释放哪块空间,就把那个空间的起始地址传过去
free只能释放动态开辟的,指向的空间若不是动态开辟的,那free的函数行为是未定义的
如果传的是空指针,那free什么都不做
free也要包含头文件stdlib.h
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(20);//使用时自己决定类型
if (p == NULL)//判断开辟成功没有
{
perror("malloc");//空指针,开辟失败,报错
return 1;
}
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = i + 1;//这样写就是让p就是起始地址
}
//释放内存 free
free(p);//传递给free的是要释放的空间的起始地址
p = NULL;//避免P成为野指针,释放完就设成空指针是好的编程习惯
return 0;
}
3.calloc——也还用来动态申请内存的
void*calloc (size_t num,size_t size)
开辟num个大小为size的空间,并把空间初始化为0
int main()
{
int*p=(int*)malloc(5 * sizeof(int));//malloc总大小
if (p == NULL)
{
perror("malloc");
return 1;
}
int* p = (int*)calloc(5 , sizeof(int));//calloc中间就是逗号了
return 0;
}
malloc和calloc就这点区别,malloc不初始化
剩下的使用都一样,都相当于数组
calloc就是会把空间初始化为0
4.realloc——让动态内存管理更灵活
void*realloc (void*ptr,size_t size)
ptr——是要调整的内存地址
size——调整之后的新大小
返回调整之后的内存起始地址
情况1.在后面分配20个字节就行
情况2.后面不够20,直接在后面找个地方申请40个
对于情况2::
1.在堆区内存在找一个新的空间,满足大小要求
2.会将原来空间的数据拷贝一份到新的空间
3.释放2旧的空间(realloc自己释放,不用free)
4.返回新的内存空间的起始地址
情况3.调整失败,返回NULL
#include<stdlib.h>
#include<stdio.h>
//int main()
//{
// int* p = (int)malloc(20);//使用时自己决定类型
// if (p == NULL)//判断开辟成功没有
// {
// perror("malloc");//空指针,开辟失败,报错
// return 1;
// }
// int i = 0;
// for (i = 0; i < 5; i++)
// {
// *(p + i) = i + 1;
// }
// //释放内存 free
// free(p);
// p = NULL;
//
// return 0;
//}
//向内存申请5个整型空间
//int main()
//{
//int*p=(int*)malloc(5 * sizeof(int));//malloc总大小
//if (p == NULL)
//{
// perror("malloc");
// return 1;
//}
// int* p = (int*)calloc(5 , sizeof(int));//calloc中间就是逗号了
// int i = 0;
// for (i = 0; i < 5; i++)
// {
// printf("%d", *(p + i));
// }
// free(p);
// p = NULL;
// return 0;
//}
int main()
{
int* p = (int*)malloc(5 * sizeof(int));//20
if (p == NULL)
{
perror("malloc");
return 1;
}
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = i + 1;
}
//希望空间调整为40个字节
int*ptr=(int*)realloc(p,40);
if (ptr != NULL)
{
p = ptr;//确定调整空间大小成功了,再把40个字节给p,要不然失败了,之前的20个也没了
}
return 0;
}
int main()
{
int* p = (int*)malloc(5 * sizeof(int));//20
if (p == NULL)
{
perror("malloc");
return 1;
}
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = i + 1;
}
//希望空间调整为40个字节
int*ptr=(int*)realloc(p,40);
if (ptr != NULL)
{
p = ptr;//确定调整空间大小成功了,再把40个字节给p,要不然失败了,之前的20个也没了
int i = 0;
for (i = 5; i < 10; i++)
{
*(p + i) = i + 1;//给后面20个字节
}
for (i = 0; i < 10; i++)
{
printf("%d ",*(p+i));
}
free(p);
p = NULL;
}
else {
perror("realloc");
free(p);
p = NULL;
}
return 0;
}
常见的动态内存管理的错误
1.对空指针的解引用操作——malloc的返回值一定要判断
int main()
{
int* p = (int*)malloc(20);
*p = 20;//没有判断返回值
return 0;
}
修改后
int main()
{
int* p = (int*)malloc(20);
if(p==NULL)
{
perror("malloc");
return 1;
}
*p = 20;
return 0;
}
2.对动态开辟空间的越界访问
//对动态开辟空间的越界访问
#include<stdlib.h>
void test()
{
int i = 0;
int* p = (int*)malloc(10 * sizeof(int));
if (NULL == p)
{
exit(EXIT_FAILURE);
}
for (i = 0; i <= 10; i++)//i<10就可以了,i<=10越界
{
*(p + i) = i;//当i是10的时候越界访问 ,会指向第11个
}
free(p);
}
int main()
{
test();
return 0;
}
3.对非动态开辟内存使用free释放
void test()
{
int a = 10;
int* p = &a;
free(p);
p=NULL;//a在栈区,只是个局部变量,非动态开辟
}
4.使用free释放动态内存空间的一部分
//free释放动态开辟的一部分
int main()
{
int* p = (int*)malloc(100);
if (p == NULL)
{
return 1;
}
int i=0;
for (i = 0; i < 5; i++)
{
*p = i+1;
p++;//p一开始指向的是100空间的起始地址
}
free(p);//这里的p不再是动态申请的内存的起始位置
p = NULL;
return 0;
}
5.对同一块动态内存多次释放
//同一块内存多次释放
void test()
{
int* p = (int*)malloc(100);
free(p);
free(p);
}
可以修改为
//同一块内存多次释放
void test()
{
int* p = (int*)malloc(100);
free(p);
p=NULL;
free(p);//这里p是空指针在释放没问题,free什么都不用做
}
6.动态内存忘记释放(内存泄漏)
//忘记释放
void test()
{
int* p = (int*)malloc(100);
if (NULL != p)
{
*p = 20;
}
}//没有释放后面在想用找不到了,存到局部变量里了,生命周期完成后,局部变量销毁
int main()
{
test();
while (1);
}