C语言:动态内存管理(一)

一、为什么要有动态内存分配

int val=20;//在栈空间开辟四个字节
char ch[10]={0};//在栈空间开辟10个字节的连续空间

    以上为C语言内存开辟空间的两种方式,这两种方式有以下两个特点:

01.空间开辟的大小是固定不变的

02.数组在创建的时候,必须指定数组的长度,数组空间一旦确定了就无法调整大小

    这两个特点注定会导致一个缺点:当以上举例的字符数组不需要完全利用10个字节空间的时候,会造成内存浪费。

    因此,C语言引入了动态内存开辟让程序员可以自己申请和释放空间,大大提高了使用内存的灵活性。

二、动态内存分配函数

    C语言的动态内存分配操作借助于一类动态内存分配函数,这里简单介绍一些常见的函数

 2.1.void* malloc (size_t size)

#include<stdlib.h>
void*malloc(size_t size)
功能:根据用户需要在堆区开辟空间,并且空间是连续的
参数:要开辟空间的大小(单位:字节)
返回值:
这里分别为两种情况:
01.开辟失败,返回NULL
02.开辟成功,返回开辟好的空间的首地址

    注意事项:

01.在创建一个指针变量(以下代码中的arr)并将malloc()的返回值赋值给这个指针变量时,由于malloc()的返回值有可能为NULL,因此在使用指针变量之前,需要检查是否成功开辟了空间

int main()
{
//为包含10个整型元素的整型数组arr开辟空间
int *arr=(int*)malloc(10*sizeof(int));
//检查指针是否为空指针
if(arr==NULL)
{
perror("malloc");
return 1
}
//使用
.......

return 0;
}

2.2void free(void*ptr)

#include<stdlib.h>
void free(*ptr)
功能:释放堆区开辟的空间
参数:堆区所开辟空间的首地址

注意事项:

01.ptr指向的空间不是动态开辟的,则行为未定义,也可以理解为这个ptr指向的空间必须是堆区里开辟的

02.如果ptr是NULL,即空指针,则此函数不执行任何操作

free的实际作用:

int main()
{
//为一个包含10个整型元素的数组开辟空间
int *arr=(int *)malloc(sizeof(int)*10);
//检查arr是否为空指针
if(arr==NULL)
{
return 1;
}
//使用
......
//使用结束,不需要再使用了,则要释放掉这块空间,节省内存
free(arr);
arr=NULL;//虽然arr指向的这块空间被释放了,但是arr仍然指向这块空间的首地址,为了避免arr成为野指针,将arr置为NULL

return 0;
}

2.3void*calloc(size_t num,size_t size)

简单介绍:

其实这个calloc函数与前面所学的malloc函数作用类似,即动态开辟空间,在此功能上,calloc函数不过是引入了一个初始化空间存储的内容为0的作用而已。

#include<stdlib.h>
void*calloc(size_t num,size_t size)
功能:函数的功能是为num个大小为size(单位:字节)的元素开辟一块空间,并把每个字节初始化为0
参数:元素个数,每个元素的大小(字节)
返回值:
情况一:开辟成功,返回开辟空间的首地址
情况二:开辟失败,返回NULL

使用情况:

2.4void*realloc(void*ptr,size_t size)

简单介绍:

有些时候,我们使用malloc或者calloc函数所动态开辟的空间不够用,在之前我们就说过,动态内存分配是一个很灵活的开辟空间方式,而这个realloc函数就是其灵活的关键,为什么这么说,因为他可以根据需要扩大之前所开辟的空间,并且不会影响你之前所存储的数据。

#include<stdlib.h>
void*realloc(void*ptr,size_t size)
功能:开辟完的空间觉得不够用,可以用此函数扩大空间
参数:需要扩容空间的首地址,全新的空间大小(单位:字节)
返回值:
情况复杂,下面介绍

realloc函数的返回值:

realloc的返回值在某些情况不同于之前的malloc和calloc函数,但也有相同之处。

不同之处:

realloc函数返回值的不同之处体现在其成功开辟空间的时候,这里分为2种情况,在介绍这两种情况之前,先让我给大家引入一个情境:

由于堆区空间是一块一块区域分配的,你也可以将堆区空间理解为一大块田地,而这块田地分配给了不同的农民,诸如张三有2亩地,李四有4亩地,王五有8亩地......,不同有不同的地址

他们的田地划分在可能是这样子的:

让我接接着讲,张三同志通过辛勤劳动积累了不少财富,是时候扩大自己的土地了......

两种情况:

01.张三同志其实只需要扩大那么一点点土地而已,不需要那么大,具体表现为不会扩大到李四王五所属的土地。

张三同志向土地划分机关realloc函数申请扩大土地,机关根据张三同志的需要,给其重新划分了土地,由于其扩大的区域不大,张三同志的地址仍然不变,新的地址也是原来的地址

02.张三同志的野心已经随着财富积累逐渐变大,他想要一块更大的空间,大到什么程度?会扩大到涵盖李四王五所属的土地。

这事李四可不同意,所以张三向realloc函数申请扩大土地的时候,realloc函数的处理方式是这样的:张三同志,鉴于您所扩大的土地也许会涵盖李四的土地,这样吧,原来的土地你也不用再使用了,机关给你重新划分一块更大的土地给您。

换句话说,张三同志要搬到另一个地方去种地了,但是原来地上的农作物咋整?没关系,原封不动全给你移植到新土地上,也就是说,之前开辟空间存储的数据都会被保存下来,这就是realloc函数的厉害之处!

一代农民张三的奋斗史讲完了,我们再谈谈realloc函数与malloc和calloc函数的向他之处

相同之处:

当空间开辟失败,返回NULL

看似平平无奇,其实这个点的往往有坑!

int main()
{
//开辟空间
int *arr=(int*)malloc(sizeof(int)*10);
//检查
if(arr==NULL)
{
return 1;
}
//空间不够,再开辟
arr=(int*)realloc(arr,sizeof(int)*20);

//使用结束,释放空间
free(arr);
arr=NULL;

return 0;
}

这个代码有一个问题?就是当你使用realloc函数开辟空间的时候,开辟成功了还好,如果开辟失败,那么,arr就变成了空指针,原来空间所存储内容再也找不到;而且没有了地址,也无法释放动态开辟的空间。

以下是一种改进方法:

int main()
{
//开辟空间
int *arr=(int*)malloc(sizeof(int)*10);
//检查
if(arr==NULL)
{
return 1;
}

//空间不够,再开辟
int *tmp=NULL;//创建一个指针作为中介
tmp=(int*)realloc(arr,sizeof(int)*20);
if(tmp==NULL)//开辟失败,释放arr所指向的空间,arr置为NULL,提前结束函数
{
free(arr);
arr=NULL;
return 2;
}
arr=tmp;//开辟成功,将tmp的值赋给arr

//使用结束,释放空间
free(arr);
arr=NULL;
tmp=NULL;

return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值