动态内存管理知识详解

动态内存管理篇

一、动态内存函数

动态内存函数需要包含头文件stdlib.h

与开辟静态内存空间(在栈区)不同,动态内存函数在堆区申请或释放内存空间。

malloc函数

malloc函数用来申请一块内存空间。

void* malloc(size_t size);

参数size表示要开辟的内存的字节数。要开辟多少个字节的内存,就传数字几。

返回类型为void*类型,因为malloc函数不清楚我们开辟这块空间要存储什么类型,所以使用时我们要根据需要对这个返回值进行强制类型转换,比如:

int* pi = (int*)malloc(40);

不过,malloc函数申请内存空间时可能会失败,失败后返回NULL,这时候我们要对malloc函数的返回值进行检查。

不要让malloc函数的参数为0,这种行为是未定义的,取决于编译器。

如果我们频繁、大量地开辟空间,会影响程序运行时间,还会导致堆区内存碎片化。

free函数

free函数用来释放动态内存空间,我们要养成释放多余空间的习惯,避免出现内存泄漏的问题。我们练习时执行的程序运行时间很短,程序结束时会自动释放(空间,而对于大的工程,需要不断地运行,这时候就需要free函数及时释放内存空间。

内存泄露

指在编程过程中,一块已分配的内存空间在使用完毕后没有及时释放,使得这块空间无法被再次使用,造成系统内存浪费和性能下降,甚至最终会导致系统崩溃的现象。

简单来说,就是我申请了一块空间,使用后没有释放,导致这块空间被占用又无法再被分配使用。

void free(void* ptr);

使用free函数时,传入的指针指向的必须是开辟的一块动态内存空间,free函数会把这块空间释放,不过释放后,传入的指针指向的地址还是之前的地址,释放后的空间不属于我们,所以我们要及时将传入的指针变量置空NULL

如果free函数的参数为NULL,那么free函数什么事都不做。

int pi = (int*)malloc(40);
free(pi);
pi = NULL;

calloc函数

calloc函数也可以动态内存开辟。

void* calloc(size_t num, size_t size);

功能为:为num个大小为size的元素开辟一块空间,同时将空间的每个字节初始化为0。

从结果上看,calloc函数和malloc函数的区别就是calloc函数在开辟空间时会初始化。

int* pi = (int*)calloc(10, sizeof(int));
if(pi == NULL)
{
    printf("%s\n",strerror(errno));
    return 1;
}

我们在选择使用malloc函数还是calloc函数时,只需要考虑是否想对开辟的空间初始化

这两个函数可以这样理解:calloc = malloc + memset

realloc函数

realloc函数用来对动态开辟内存大小进行调整。

void* realloc(void* ptr, size_t size);
  • ptr就是要调整的动态内存的起始地址,指向要调整的内存空间。
  • size是我们希望将ptr指向的内存空间调整成的内存大小,单位也是字节。
  • realloc返回类型为void*,与malloc和calloc函数一样,我们需要进行强制类型转换。
int* pi = (int*)malloc(40);
if(pi == NULL)
{
    printf("%s\n",strerror(errno));
    return 1;
}
for(int i = 0; i < 10; i++)
{
    *(pi + i) = i + 1;
}
int* pi_tmp = (int*)realloc(pi, 80);

前面我们没有介绍详细realloc函数的返回值,这里我们进行介绍。

我们假设要求realloc函数要将40个字节的动态内存空间扩容成80个字节的空间,这时候有两种情况:

  • 当原内存空间后面的内存空间足够大。 realloc函数会在原空间后面接着再开辟40个内存空间,与原内存空间连续。这时候realloc返回的是原内存空间的起始地址。
  • 当原内存空间后面的内存空间不够大。 为了避免把其他正在使用的内存空间的数据覆盖,realloc函数会在内存空间中重新找一块连续的80个字节的内存空间,并将原来内存空间存储的数据拷贝到新空间,之后自动释放原内存空间。这时候realloc函数返回的是新的内存空间的起始地址。

这时候出现了一个问题,我们用什么来接收realloc函数的返回值。既然我们使用realloc函数进行调整,,那么就不可避免地会出现调整失败地情况,比如,扩容失败。这时候,realloc函数会返回NULL,如果我们使用原来内存空间的指针变量来接收,结果是,我们既调整失败,又丢失了原来内存空间的地址。

怎么解决呢?

我们创建一个新的临时指针变量来接收这个返回值,判断是否为空指针,如果返回值不是空指针(说明调整成功),我们再把临时变量存储的地址传给原指针变量。

int* pi = (int*)malloc(40);
if(pi == NULL)
{
    printf("%s\n",strerror(errno));
    return 1;
}
for(int i = 0; i < 10; i++)
{
    *(pi + i) = i + 1;
}
int* pi_tmp = (int*)realloc(pi, 80);
if(pi_tmp != NULL)
{
    pi = pi_tmp;
}
//使用后记得释放
free(pi);
pi = NULL;

realloc函数除了调整功能外,还有别的用处,就是模拟malloc函数。

我们再来看一下realloc函数的格式:

void* calloc(size_t num, size_t size);

第一个参数要求传入一个指向要调整的内存空间的指针,如果我们传入NULL作为calloc的第一个参数,这时候,realloc相当于是malloc

例如,realloc(NULL, 40); 等价于 malloc(40);

二、常见动态内存错误

对NULL进行解引用操作

int* pi = (int*)malloc(1000000);
*pi = 10;//如果开辟失败,malloc函数会返回一个NULL

所以我们要对返回值进行判断:

int* pi = (int*)malloc(1000000);
if(pi == NULL)
{
    return 1;
}
*p = 10;
free(pi);
pi = NULL;

越界访问动态内存空间

int* pi = (int*)malloc(40);
if(pi == NULL)
{
    return 1;
}
for(int i = 0; i < 11; i++)//循环了11次,越界访问
{
    *(p + i) = i + 1;
}
free(pi);
pi = NULL;

对非动态内存空间使用free函数

这种行为显然错误,我们要避免。

用free函数释放动态内存的一部分

int* pi = (int*)malloc(40);
pi++;
free(pi);//error,pi已经自增过了,不是指向完整的动态内存空间了,pi此时存放的地址是动态内存空间起始地址后4个字节的地址

对同一块动态内存多次释放

int* pi = (int*)malloc(40);
free(p);
free(p);//err,多次释放

内存泄漏

介绍free函数时一并介绍了,一定要记得及时释放动态开辟的内存空间。

  • 31
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值