目录
为什么存在动态内存的分配?
我们已经掌握的内存开辟方式有:
int a=10;//在栈空间上开辟4个字节的空间
char arr[10]={0};//在栈空间上开辟10个字节的连续空间
但是上述的开辟空间的方式有两个特点:
1. 空间开辟大小是固定的。
2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。 这时候就只能试试动态存开辟了。
动态内存的开辟发生在堆区上面:
malloc函数
void *malloc( size_t size );
函数功能:malloc函数的功能是开辟指定字节大小的连续可用的内存空间
函数参数:只有一个参数,传入的是需要开辟的字节个数
返回值:如果开辟成功返回该空间的首地址,如果开辟失败则返回一个NULL
例如我们要开辟一个5个整型的空间:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* ptr = (int*)malloc(5 * sizeof(int));//强制类型转化成int*
if (ptr == NULL)
{
printf("开辟失败\n");
}
else
{
printf("开辟成功\n");
free(ptr);//释放ptr
ptr = NULL;//ptr置成空指针
}
return 0;
}
注意:malloc函数开辟的空间不对空间做任何处理,即所有的数据内容为随机值
calloc函数
void *calloc( size_t num, size_t size );
函数功能:calloc函数的功能也是开辟指定字节大小的连续可用的内存空间,并且把空间每个字节置0
函数参数:第一个参数是开辟的内存用于存放的元素个数,第二个参数是各个元素的大小,单位为字节
返回值:如果开辟成功返回该空间的首地址,如果开辟失败则返回一个NULL
calloc和malloc的用法大同小异:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* ptr = (int*)calloc(5, sizeof(int));
if (ptr == NULL)
{
printf("开辟失败\n");
}
else
{
printf("开辟成功\n");
free(ptr);
ptr = NULL;
}
return 0;
}
注意:calloc函数与malloc函数的最大区别在于,calloc函数开辟好内存后会将空间内容中的每一个字节都初始化为0。
realloc函数
realloc函数的出现让动态内存管理更加灵活。 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小做灵活的调整。
void *realloc( void *memblock, size_t size );
函数功能:调整已经开辟好的空间的字节大小
函数参数:第一个参数是需要调整大小空间的内存的首地址,第二个参数是动态内存调整后的新大小,单位字节
返回值:如果开辟成功返回该空间的首地址,如果开辟失败则返回一个NULL
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* ptr = (int*)calloc(5, sizeof(int));
if (ptr == NULL)
{
printf("开辟失败\n");
}
else
{
printf("开辟成功\n");
//在此处进行扩容
int* p = (int*)realloc(ptr, 100);
if (p != NULL)
{
ptr= p;//扩容成功
}
free(ptr);
ptr = NULL;
}
return 0;
}
使用realloc扩容时有三种情况:
第一种:
第二种:
第三种:
需扩展的空间后方没有足够的空间可供扩展,并且堆区中也没有符合需要开辟的内存大小的空间。结果就是开辟内存失败,返回一个NULL。
free函数——防止内存泄漏
void free( void *memblock );
函数功能:将malloc、calloc以及realloc函数申请的动态内存空间释放,返还给操作系统
函数参数:要释放空间的首地址
返回值:无
free函数的使用就是在动态内存开辟完后,使用下面两行代码:
free(p);//p为要释放的代码块的首地址
p = NULL;
注意:如果参数 p指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 p 是NULL指针,则函数什么事都不做。
如果p未置为NULL,那么p就变成野指针,这是非常危险的
如果在使用完动态内存后忘记将其空间释放,便会造成内存泄漏的问题:
内存泄漏(MemoryLeak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存泄漏缺陷具有隐蔽性、积累性的特征,比其他内存非法访问错误更难检测。因为内存泄漏的产生原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃。
常见的动态内存错误:
1、对NULL指针解引用
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
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释放
void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}
4、使用free释放一块动态开辟内存的一部分
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置,p指针已经移动了
}
5、对同一块动态内存多次释放
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
6、动态开辟内存忘记释放(内存泄漏)
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
忘记释放不再使用的动态开辟的空间会造成内存泄漏。
切记:动态开辟的空间一定要释放,并且正确释放!
谢谢各位博友们的观看!