堆内存

本文详细介绍了堆内存的使用,包括malloc、calloc、realloc和free等函数的运用,强调了堆内存手动管理的必要性和挑战,如内存泄漏和碎片。同时,探讨了如何避免内存泄漏,减少内存碎片的方法,以及递归调用对内存的影响。此外,还提及了堆内存定义二维数组和函数递归的基本原理。最后,讨论了堆内存越界和内存泄漏的后果以及定位内存泄漏的策略。
摘要由CSDN通过智能技术生成

堆内存

是进程的一个内存段(text\data\bss\heap\stack),是由程序员手动管理
特点:足够大
缺点:使用麻烦
一、为什么使用堆内存
1、随着程序的复杂数据只会越来越多
2、其他内存段的申请释放不受控制,堆内存的申请释放是受控制的
二、如何使用堆内存
C语言中没有控制堆内存的语句,只能用C标准库提供的函数
 1.void *malloc(size_t size);
    功能:从堆内存申请一块连续的size个字节的内存,申请到的内存存储的
    	是什么内存不确定
    返回值:成功返回申请到的内存的首地址,失败返回NULL
    注意:void*在c++编译器中不能自动类型转换为其他类型,如果想要让代码
    也能在C++中兼容需要强制类型转换
    int* p = (int*)malloc(4);
2.void free(void *ptr);
    功能:释放一块堆内存,可以释放NULL,但是不能重复释放和释放非法内存
    注意:释放仅仅只是回收使用权,里面的数据不会被特意清理
3.void *calloc(size_t nmemb, size_t size);
    功能:从堆内存中申请nmemb块size个字节的内存,申请到的内存块会被
    	初始化为0;
    注意:申请到的依旧是一块连续的内存
4.void *realloc(void *ptr, size_t size);
    功能:改变已有内存块的大小,size表示改变后的大小而不是要改变多少
    返回值:是调整后的内存块的首地址,一定要重新接收该函数的返回值,
    	因为可能不是在原基础上调整的,如果无法在原基础上调整:
            1、申请一块size大小的内存
            2、把原内存上的内容拷贝过去
            3、把原内存释放,再返回新内存的首地址
三、malloc的内存管理机制
当首次向malloc申请内存时,malloc会向操作系统申请,操作系统会直接给33页
(1页=4096字节)内存交给malloc管理
但是不意味着你可以越界访问该内存,因为malloc也会把内存分配给“其他人”,
这样会产生脏数据

每次申请的内存块之间是有一些空隙(4~12字节),这些空隙是为了内存对齐,
其中一定有4个字节记录malloc的维护信息,这些维护信息决定了下一次malloc时
分配内存的位置,也可以借助这些信息计算出内存块的大小,破坏这些信息会影响
下一次malloc以及free
四、使用堆内存需要注意的问题
内存泄漏
内存无法使用,也无法释放,而再次使用时只能重新申请,然后重复
以上过程,日积月累系统中可用的内存会越来越少
    注意:程序一旦结束属于它的资源都会被操作系统回收
    
如何避免内存泄漏:
        谁申请的谁释放,谁知道释放谁释放

如何定位内存泄漏:(上网搜索)
    1、查看内存使用情况 ps -aux
    2、分析代码,使用代码分析工具检查malloc的书写情况
    3、封装malloc、free,记录申请、释放的信息到日志
内存碎片
    已经释放但无法继续使用的内存叫内存碎片,由于申请和释放的
    时间不协调导致的,内存碎片是无法避免的,只能尽量减少

    如何减少内存碎片:
        1、尽量使用栈内存
        2、不要频繁的申请和释放内存
        3、尽量申请大块内存自己管理
内存清理函数
 void bzero(void *s, size_t n);
功能:把一块内存清理为0
s:首地址
n:内存块的字节数


 void *memset(void *s, int c, size_t n);
功能:把内存按 字节 设置为c
s:内存块首地址
c:想要设置的ASCII码值
n:内存的字节数
返回值:成功返回设置后的内存的首地址,失败返回NULL
五、堆内存定义二维数组:
指针数组:
    类型* arr[n];
    //指针数组
    int* arr[n] = {};
    for(int i=0;i<n;i++)
    {
        arr[i] = malloc(sizeof(类型)*m);//容易产生内存碎片
    }
    注意:每一行的m可以不同,就可以形成不规则的二维数组
数组指针:
    类型 (*arrp)[列数] = malloc(sizeof(类型)*行数*列数)//对内存要求高

    注意:所谓的多维数组都是用一维数组来模拟的
六、函数递归:
分治:
    函数自己调用自己的行为叫做递归,会产生死循环
    递归可以实现分治这种算法,把一个复杂的大问题分解成若干个相同的小问题,
    直到全部问题解决

1、出口
2、分解成小问题来解决
3、调用自己

递归函数每次调用时都会在栈内存产生一份拷贝,直到达到出口,
才一层一层地倒着释放,因此递归会非常耗费内存,
与循环相比速度非常慢,因此能使用循环解决的问题就不要使用递归
    递归的优缺点:
    1、耗费内存、速度慢
    2、好理解、思路清晰
七、常见的笔试面试题:
1、堆内存与栈内存的区别
定义:
 什么是堆内存
 heap  堆内存: 由程序员手动管理,比较麻烦,但是足够大
 什么是栈内存
 stack 栈内存: 存储局部变量、块变量,由操作系统自动申请、
 释放内存,大小有限,超过限制会产生段错误
                
 区别:
  大小、使用、安全性...
2、堆内存越界的后果
1、破坏malloc的维护信息,导致接下去的malloc和free出错
2、超过33页会产生段错误
3、其余可能会出现脏数据
3、什么是内存泄漏,如何定位内存泄漏
由于业务逻辑出错或者粗心大意导致已经使用完毕的堆内存没有及时释放,当再次
需要使用时又重新申请堆内存,又没有释放,长期以往导致可用的堆内存越来越少,
系统越来越慢甚至崩溃,这种情况叫做内存泄漏

  定位:
  1、windows下查看任务管理器,linux通过Ps -aux查看内存使用情况
  2、通过类似于mtrace代码分析工具,分析代码是否释放内存
  3、封装malloc、free,记录申请、释放的信息到日志
4、什么是内存碎片,如何减少内存碎片
已经释放但是无法使用的内存叫做内存碎片,是由于申请、释放的时间、大小不协调导致的
 如何减少内存碎片:
   1、尽量使用栈内存
   2、尽量申请大块内存自己管理
   3、不要频繁的申请释放内存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值