动态内存管理(第一次使用画图工具版)

C语言中,我们常常都是在栈区中开辟空间的,就比如我下面的代码

int main()
{
    int a = 0;
    int arr[10];
}

以上的两行代码,分别向栈区申请了4个字节和40个字节的空间,可是我们有没有想过,申请这么多空间,万一我们用不上了怎么办,那岂不是浪费了吗?对的,借着这个问题,我们来聊一聊动态内存,所以“动态”,就是内存空间是可依据程序员的想法可作变动的空间

我们先来将两个函数

malloc和free

malloc————————————————————————————————
这是c语言提供的一个用于动态内存开辟的函数:

void * malloc(size_t size),size单位是字节

  1. 如果开辟成功,则返回一个指向该空间起始位置的地址
  2. 如果开辟失败,则返回空指针NULL
  3. 返回值是void *,所以malloc开辟的空间没有指定的什么类型,后面需要将其转换成什么类型,进行强制类型转换即可
  4. 如果size的值为0,那此时malloc的行为是未定义的,取决于编译器

接下来,举个例子

int main()
{
    int * p = (int *)malloc(40);//malloc()里面存放40,意味着我要从堆区中开辟一个40字节长的空间,因为malloc返回的是一个void *的指针,我们要对这块内存空间存放整型,所以我们要给他转换成int *的指针,然后利用指针挨个访问
    
    if(p==NULL)//malloc是有可能开辟失败的,所以要对其返回的指针进行判断,不为空指针才可以继续运行
    {
       perror("malloc");//打印开辟失败的原因
       return 1;
    }
    for(int i = 0;i<10;i++)
    {
       *(p+i) = i+1;//对malloc开辟的空间,利用整型指针进行逐个赋值
    }
    return 0}

在这里插入图片描述
在这里插入图片描述

上面就是malloc函数的简单运用了,还是比较简单的,但是值得一提的是,动态内存是在堆区里开辟的空间,其与我们之前学过的局部变量,函数形参有所不同,后者程序会自动回收这块空间,而前者的空间回收通常有以下两种方法:1.使用free函数释放2.程序结束自动释放

那么接下来,我们来讲一讲free函数
free————————————————————————————————
括号里面填指向该被需要释放的动态内存的起始地址(也就是指针);

void free(void * ptr);

  1. 如果参数ptr指向的空间不是动态开辟的,则free函数的行为未定义
  2. 如果参数ptr是NULL指针,则函数什么是都不做

接下来,上代码

int main()
{
    int * str = (int *)malloc(40);向动态内存开辟40个字节的空间
    if(str==NULL{
       perror("malloc");
       return 1;
    }
    free(str);//回收str指向的动态内存空间
    str = NULL;//这里的str需要置空,因为动态内存空间已经被回收了,但是str还是指向了那一块空间,相当于野指针,对其解引用就相当于非法访问内存了,所以要将其置空
    return 0}

这里讲一下,为什么动态内存需要及时回收,我这里给一个极端的代码,从下方的代码可以看出,程序进入主函数后,调用了test函数,进而向堆区开辟了40个字节的空间,然后进行了while,但是whlie循环不退出,一直循环,所以上述给的两个释放动态内存的方法都没有用到(free函数和程序结束时自动回收),所以程序一直霸占着这块空间,也没人去去释放它,假设很多程序员都这么做呢?就会把整块堆区里面可以供给开辟动态内存的空间耗干,进行使得电脑运行起来卡顿,影响性能,所以我们在开辟动态内存时要及时地释放内存

void test()
{
   int * p = (int *)malloc(40);
}
int main()
{
   test();
   while(1)
   {
      ;
   }
   return 0}

calloc 和 realloc

calloc—————————————————————————

C语言还提供了一个函数叫calloc,calloc也可以用来动态内存分配。原型如下:

void * calloc (size_t num, size_t size);

  1. 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节都初始化为0
  2. 与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为0;

举个例子:

int main()
{
    int * p = calloc(10,sizeof(int));
    if(p==NULL)
    {
        return 1;
    }
    int i = 0;
    for(i = 0;i<10;i++)
    {
       printf("%d ",*(p+i));
    }
    return 0;

本次运行的结果为:0 0 0 0 0 0 0 0 0 0,可见calloc把开辟的内存空间的每个字节都初始化了为0;

在这里插入图片描述

realloc————————————————————————

  1. realloc的出现让动态内存管理更加灵活
  2. 有时候我们会发现我们向堆区申请的动态内存太小了,那realloc函数就可以做到对动态开辟内存大小的调整。

void * realloc(void * ptr,size_t size)
ptr指向的是要被调整的动态内存的起始地址指针,size指的是重新被开辟的内存大小

我们通过几个图来了解一下realloc函数的使用逻辑(方法)。
扩展内存一般会遇到三种情况:

int main()
{
   int * p = (int *)malloc(sizeof(int));//向堆区开辟了4个字节的空间
   if(p==NULL)
   {
       return 1;
   }
   *p = 1;
   int * ptr = (int *)realloc(p,sizeof(int) + 5*sizeof(int));//在四个字节空间的基础上,又开辟了20个字节
   if(ptr==NULL)
   {
      return 1;
   }
   p = ptr;//将ptr赋给p
   ptr = NULL;/及时将ptr置为空指针
   free(p);//释放p指向的动态内存,也就是原先的加上后来增添的内存
   p = NULL;
   return 0;
   
}

(1.)向原内存后面进行扩展,也就是拼接上去
在这里插入图片描述

(2.)第二种情况,向后扩展的时候发现后面部分的内存已经有人使用了就比如下图
在这里插入图片描述

————那就需要向内存申请一块额外的空间
在这里插入图片描述

需要注意的是,原内存会被销毁,并且把原内存的数据拷贝到新内存上

(3.)第三种情况,也就是内存扩展开辟失败
这种情况下,系统会返回一个空指针(NULL),并且将原空间里面的数据全都还回去了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值