目录
1.为什么存在动态内存管理?
1.1 动态内存管理的函数
1.malloc/free
2.calloc
3.realloc
1.2 动态内存开辟时常见问题
1.对NULL的解引用 *** 作
2.free释放动态开辟内存的一部分
3.对同一块的内存的重复释放
4.对不是动态开辟内存的空间进行free
5.没有对动态开辟内存的空间free
6.对动态开辟的内存越界访问
1.为什么存在动态内存管理?
一般开辟内存空间的时候,像开辟数组、变量时开辟的都是固定的空间
//常见的开辟内存的方式
int a = 0; //向内存申请四个字节的空间,在栈上申请
int arr[10]={0}; //向内存申请连续的十个int类型的空间,在栈上申请
像上述的方式有两个特点:
1.空间开辟大小是固定的。
2.数组在声明时必须指定大小,它所需要的空间在编译时分配。
这些开辟内存的方式是在编译的时候就申请了空间,但有些时候我们在程序运行的时候才知道到底需要多少空间,这时候就只能试试动态内存开辟空间。
首先认识一下malloc这个函数
这个函数向内存申请一块连续可用的空间,并返回指向这个空间的指针
特点:
#1.malloc需要一个size来指定创建空间的大小,单位是字节
#2.malloc在申请空间成功时,会返回一个指向申请空间地址的指针
#3.在申请空间失败的时候,会返回一个NULL指针,所以在使用malloc时要进行判空 *** 作
#4.malloc返回的是一个void*的指针,所以在进行开辟空间时要转换成对应的类型
#5.malloc的参数如果是0,那么这种 *** 作是标准未定义的,取决于编译器
在使用malloc的时候,有一件事是必须要做的,那就是内存释放,C语言就很贴心的给我们准备了内存释放的函数free
free的介绍
这个函数把动态开辟的空间还给 *** 作系统
特点:
#1.如果free的参数指向的不是动态开辟的空间,那么这种 *** 作是未定义的
#2.如果free的参数是NULL,那么free什么也不做
在使用malloc的时候一定要进行free释放内存,不然会造成内存泄漏,除此之外还要把指针置为NULL,避免野指针的风险。
举个栗子:
int* ret = (int*)malloc(40);// 申请一个40个字节的动态内存
if (ret == NULL) //判断开辟空间有没有成功
{
return 1;
}
for (int i = 0; i < 10;i++)
{
*(ret+i)=i;
}
free(ret); //使用完毕,进行内存释放
ret = NULL; //将指向的空间置为NULL,避免野指针
2.calloc
calloc也是一种开辟动态内存空间的函数
特点:
1#.calloc会开辟一个num个size字节的空间
2#.calloc跟malloc唯一的区别就是在返回指针前,将开辟空间的每一个字节初始化为全0
举个栗子:
int* ret=(int *)calloc(10, sizeof(int));
if (ret == NULL) //进行判空 *** 作
{
perror("calloc"); //报错提醒
return 1;
}
free(ret); //calloc也是要进行内存释放的
ret = NULL; //避免野指针
可以看到在内存开辟完内存空间内就已经被置为全0了。
使用calloc可以方便的进行初始化,在需要初始化的时候使用calloc是不错的选择,但也不要忘了使用判空 *** 作和内存释放以及指针置为NULL。
realloc的出现让动态内存的管理更加的灵活,在我们觉得开辟的空间大了,或者开辟的空间小了,就可以使用realloc来重新定义空间的大小
函数原型:
特点
#1.ptr是要重新设置的空间的指针
#2.size是要重新设置的字节数
#3.函数会返回一个指针,这个指针指向已经重新分配好了的空间
#4.在改变原有空间的大小的情况下,原有空间的数据会被转移到新开辟的空间
#5.在开辟新空间时,一般会遇到两种情况:
情况1:原有空间后有足够的空间
情况2:原有空间后没有足够的空间,需要重新寻找新的空间
情况1:在原有空间后有足够的空间可以开辟,直接往后开辟。
情况2:当原有空间后没有空间里,realloc会在堆上寻找一块连续可用的空间来使用,并且把原有的数据转移到新的空间中,并且在转移成功后将原有空间内存释放,然后返回新的空间的地址。
当然realloc也有可能开辟失败,因为空间是有限的,而人可以进行无限的 *** 作,一个人想写bug没有人可以拦着,那怎么办呢,在realloc使用时也判断一下返回的是不是NULL就行了。
所以在使用realloc时,要注意使用的方式
举个栗子:
int* ret = (int*)malloc(40);//开辟原有内存
if (ret == NULL)//判空 *** 作
{
return 1;
}
int* ptr = NULL;
ptr=(int*)realloc(ret, 80);//在使用realloc时要创建一个临时指针
if (ptr != NULL) //因为realloc可能会开辟失败,所以为了数据的安全,在创建失败时就不改变原有空间的地址
{
ret = ptr;
}
free(ret);
ret = NULL;
ptr = NULL; //临时变量也不要忘记置为NULL
1.2 动态内存开辟时常见问题
1.对NULL的解引用 *** 作
在使用malloc,calloc,realloc进行开辟时,返回的有可能时NULL,在这个时候对NULL进行解引用的时候就会报错
int* ret = (int*)malloc(40);//开辟空间
if (ret == NULL)//判空 *** 作
{
return 1;
}
free(ret);
ret = NULL;
2.free释放动态开辟内存的一部分
在使用函数的时候,我们用来存放指向开辟空间的指针如果被改变了,那么这时候再使用free就会报错
int* ret = (int*)malloc(40);//开辟空间
if (ret == NULL)//判空 *** 作
{
return 1;
}
ret++; //改变了ret,ret不再指向开辟空间的起始地址
free(ret);
ret = NULL;
程序直接就会奔溃掉了
3.对同一块的内存的重复释放再已经free一次指针了之后,再次free一次指针,这样就会报错
int* ret = (int*)malloc(40);//开辟空间
if (ret == NULL)//判空 *** 作
{
return 1;
}
free(ret);
free(ret); //重复释放ret,报错
ret = NULL;
4.对不是动态开辟内存的空间进行free
int arr[10] = { 0 };
free(arr); //对数组进行free,驴头不对马嘴
5.没有对动态开辟内存的空间free
int* ret = (int*)malloc(40);//开辟空间
if (ret == NULL)//判空 *** 作
{
return 1;
}
不进行内存释放,大胆!这可是会造成内存泄漏的!!!不要忘记free
6.对动态开辟的内存越界访问跟数组一样,动态开辟的内存也是有会越界访问的
int* ptr=(int*)malloc(40);
if (ptr == NULL)
{
perror("malloc");
return 1;
}
for (int i = 0; i <= 10; i++)//空间一共是10个int类型的空间
{
*(ptr+i) = i; //在访问第11个元素的时候,就会越界访问
}
free(ptr);
ptr = NULL;
好了到这里就结束了,柔性数组明天出。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)