C语言动态内存分配原理以及堆区的使用(malloc、calloc、realloc、free)

C语言动态内存分配

堆和栈

  • 堆区 – 动态内存
    • 手动申请使用,使用完之后,手动释放。
  • 栈区 – 自动内存
    • 程序允许时,操作系统自动分配。
    • 通常存放普通局部变量 函数调用

动态内存调用释放的函数

#include <stdio.h>

void *malloc(size_t size);
void *calloc(size_t nmemb,size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);

malloc

  • 功能:申请动态内存

  • 参数:向操作系统申请size字节的动态内存 //size是以字节为单位

  • 返回值类型:void *

    • 万能指针(指向内存地址)
    • 操作系统储存内存必须知道内存的类型以分配内存大小,void*为了处理各种类型的变量,表示动态内存的起始位置
    • 所以我们在使用动态内存的时候需要将void* 转化为具体的类型。
  • 返回结果:

    • 失败:返回NULL
    • 成功:返回size个字节的内存的开始位置
  • 使用步骤:

    • 申请动态内存

      • 申请字符串,需要用到strcpy函数等对内存进行操作的函数。

      • 可以申请数组

        //一维数组
        int *pa = (int *)malloc(15*sizeof(int)); //== arr[15]
        
        //二维数组 和函数参数的声明类似
        int (*pb)[3] = (int *)malloc(15*sizeof(int));// ==brr[5][3]
        int (*px)[5] = (int (*)[5])pb;  //可以直接强制转化成 xrr[3][5]
        
        free(px) // px ,pb ,pa指向同一块动态内存 只用free一次就行了。 
        
    • 判断是否成功

      #include<stdio.h>
      #include<stdlib.h> // malloc 以及其他内存分配的函数基本输都在此头文件
      
      int main(){
          void *ptd = malloc(4);
          if(ptd == NULL){
              printf("malloc failed!\n");
              exit(EXIT_FAILURE); //成功exit(EXIT_SUCCESS)
          }
          int * ptr = (int *)ptd;
      }
      
    • 转化为具体类型的指针

    • 使用动态内存

    • 释放动态内存

calloc

  • 申请动态内存
    • void *calloc (size_t nmemb, size_t size);
    • 相当于 malloc(nmemb*size)
  • 官方手册中calloc和malloc的区别
    • malloc对申请的动态内存不会进行擦除,即原内存不会清零
    • 然而calloc申请到的一定进行过擦除 ,内存申请到的全部为0
    • 其余基本上都一样。

realloc

  • void *realloc(void *ptr, size_t size);

  • 功能:调整动态内存大小

  • 参数:调节的必须是malloc/calloc/realloc的返回值

  • 返回值:

    • success:调整后的动态内存的首地址
    • fail:NULL
  • realloc的底层实现:

    • 如果该内存空间后面有足够size给字节可供调节,则地址不变
    • 如果后面的内存空间不够,那就会重新分配地址并把数据一并拷贝,原来的内存将被自动释放。
  • 注意事项:

    • 一般一定要重新接收返回值。

free

  • void free(void *ptr);
  • 功能:释放内存
  • 注意:
    • 不要多次释放
    • 不要忘了释放(忘记释放会造成内存泄漏)

内存管理

内存泄漏

  • 动态内存忘记释放;延迟(时间比较久)释放,都可称为内存泄漏。
  • 造成原因:
    • 忘记释放
    • 释放动态内存的代码没有被执行到
  • 内存泄漏的检测
    • valgrind
    • gdb
  • 内存泄露的问题:
    • 程序运行时间越长,占用的内存越多,最后可能导致内存耗尽,导致程序崩溃。

动态内存分配原理(32bit操作系统为例)

  • 在程序中,第一次申请动态内存,malloc至少分配33页的内存

  • 1页 = 4096Byte = 4KBls

    #include <unistd.h>    //不是标准C语言头文件   而是linux操作系统文件  windows下也有对应的
    int getpagesize(void);    //获得一页内存的大小    字节数   4096Byte(字节) = 4KB
    
    int main(){
        char *s = malloc(1); //申请1byte
        int i;
        //打印了33页的数据
        for(i=-8;i<33*4096-8;i++){//[s-8,s+33*4096-8)   这片内存打印
            printf("%x ",s[i]);
        }
        printf("\n");
        free(s);
    }
    
  • 即使所有的动态内存都free,最开始分配的33页动态内存一定会被保留

  • 32bit处理器下,申请的动态内存自动以4字节对齐,即使申请1个字节,但其实也有8byte的空间

  • 对于每一块动态内存,都有额外的一块空间用于保存该动态内存块的信息,一般是8-12byte

    • 把这8-12byte称为动态内存块控制信息 (是否空闲,大小,下一个动态内存块)
    • 动态内存块控制信息一旦修改,在free时基本上都会导致程序崩溃
  • 每两块动态内存之间,至少相隔16byte(64bit处理器 32byte) 至少8byte的动态内存控制块信息 至少4byte的数据(malloc给用户的空间, 4byte缓冲 realloc只是增加4字节,并会不使用新的内存)

  • 使用动态内存,遵循申请几个字节,使用几个字节的原则,不要越界访问

    • 如果越界访问,可能导致各种问题
  • 如果在申请动态内存中,之前分配的33页没有用完之前,后续的malloc都是在33页内存里划分一块动态内存

  • 如果最初的33页使用完了,后续分配是以页为单位 如果发现不够,则映射一页内存,如果发现有内存页存在空闲,则回收页内存

  • 申请动态内存的过程

    • 如果第一次申请 分配33页的动态内存 然后划出一块内存(以及8字节动态内存块) 返回用户一块可以使用的内存(一般都在控制信息块后面)
    • 如果不是第一次,则从已经动态内存块中查找空闲(被free)动态内存块且大小合适(动态内存块的大小不小于用户申请内存的大小)的内存块返回给用户,且设置为忙碌(非空闲状态)
    • 如果目前没有合适的,则从后面的动态内存中划分一块合适的内存
    • 如果后面内存不足,则进行页映射(分配新的一页)
    • 如果在free过程中,发现多块动态内存处于空闲状态,则合并动态内存块
    • 如果发现页空闲(除最开始33页外),则回收页(取消页映射)
    • 以上的目的是为了提高分配申请的效率

内存碎片

  • 动态内存在不断地申请和释放的过程中,导致内存块之间的一些内存永远也无法被使用到,称为内存碎片
  • 如何减少内存碎片,小内存使用栈,大内存使用堆 及时释放没必要的动态内存
  • 在重复申请和释放的过程中,有一些大的内存块分配给小于该内存块字节数据的用户申请使用,导致部分内存无法被使用,----内存碎片
  • 为什么小块内存不要用堆内存
    • 分配耗时
    • 每一块动态内存都需要额外至少8byte的动态内存块控制信息,内存的利用率不高
    • 容易造成内存碎片

什么时候使用动态内存

  • 不清楚需要使用多少内存,动态变化的内存 栈内存大小是固定的

  • 内存比较大时 小内存建议使用栈

  • 项目中,存储数据往往都是使用动态内存

用,----内存碎片

  • 为什么小块内存不要用堆内存
    • 分配耗时
    • 每一块动态内存都需要额外至少8byte的动态内存块控制信息,内存的利用率不高
    • 容易造成内存碎片
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时而癫狂的匡匡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值