欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

[笔记]堆和规范的堆操作 //规范的动态资源操作

程序员文章站 2022-06-03 17:00:27
堆区(heap): 用于动态内存分配。堆在内存中位于bss区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时有可能由OS回收。· - 3.1.2 栈和堆的区别 前面已经介绍过,栈是由编译器在需要时分配的,不需要时自动清除的变量存储区。里面的变量通常是局部变量、函数参数等。堆是由mall ......

堆区(heap): 用于动态内存分配。堆在内存中位于bss区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时有可能由os回收。·

-

3.1.2 栈和堆的区别

前面已经介绍过,栈是由编译器在需要时分配的,不需要时自动清除的变量存储区。里面的变量通常是局部变量、函数参数等。堆是由malloc()函数(c++语言为new运算符)分配的内存块,内存释放由程序员手动控制,在c语言为free函数完成(c++中为delete)。栈和堆的主要区别有以下几点:

(1)管理方式不同。

栈编译器自动管理,无需程序员手工控制;而堆空间的申请释放工作由程序员控制,容易产生内存泄漏。

(2)空间大小不同。

栈是向低地址扩展的数据结构,是一块连续的内存区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,当申请的空间超过栈的剩余空间时,将提示溢出。因此,用户能从栈获得的空间较小。

堆是向高地址扩展的数据结构,是不连续的内存区域。因为系统是用链表来存储空闲内存地址的,且链表的遍历方向是由低地址向高地址。由此可见,堆获得的空间较灵活,也较大。栈中元素都是一一对应的,不会存在一个内存块从栈中间弹出的情况。

(3)是否产生碎片。

对于堆来讲,频繁的malloc/free(new/delete)势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低(虽然程序在退出后操作系统会对内存进行回收管理)。对于栈来讲,则不会存在这个问题。

(4)增长方向不同。

堆的增长方向是向上的,即向着内存地址增加的方向;栈的增长方向是向下的,即向着内存地址减小的方向。

(5)分配方式不同。

堆都是程序中由malloc()函数动态申请分配并由free()函数释放的;栈的分配和释放是由编译器完成的,栈的动态分配由alloca()函数完成,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行申请和释放的,无需手工实现。

(6)分配效率不同。

栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行。堆则是c函数库提供的,它的机制很复杂,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大的空间,如果没有足够大的空间(可能是由于内存碎片太多),就有需要操作系统来重新整理内存空间,这样就有机会分到足够大小的内存,然后返回。显然,堆的效率比栈要低得多。

 

操作系统堆管理器管理:

堆管理器是操作系统的一个模块,堆管理内存分配灵活,按需分配。
 大块内存:

  堆内存管理者总量很大的操作系统内存块,各进程可以按需申请使用,使用完释放。
 程序手动申请&释放:

  手工意思是需要写代码去申请malloc和释放free。
 脏内存:

  堆内存也是反复使用的,而且使用者用完释放前不会清除,因此也是脏的。
 临时性:

  堆内存只在malloc和free之间属于我这个进程,而可以访问。在malloc之前和free之后
      都不能再访问,否则会有不可预料的后果。

 

#include <stdio.h>
#include <stdlib.h>
 
 
int main(void)
{
    int *p = (int *)malloc(20);
    // 第二步:检验分配是否成功
    if (null == p)
    {
        printf("malloc error.\n");
        return -1;
    }
    
    *(p+3) = 12;
    *(p+300000) = 1234;
    printf("*(p+3) = %d.\n", *(p+3));
    printf("*(p+300000) = %d.\n", *(p+300000));        
    


/*
    int *p1 = (int *)malloc(4);        // p2-p1 = 0x10 = 16byte
    int *p2 = (int *)malloc(4);

    printf("p1 = %p.\n", p1);        // p2-p1 = 0x10 = 16byte
    printf("p2 = %p.\n", p2);
*/
    
/*
    int *p1 = (int *)malloc(0);
    int *p2 = (int *)malloc(0);

    printf("p1 = %p.\n", p1);        // p2-p1 = 0x10 = 16byte
    printf("p2 = %p.\n", p2);
    */
/*
    // 需要一个1000个int类型元素的数组
    
    // 第一步:申请和绑定
    int *p = (int *)malloc(1000*sizeof(int));
    // 第二步:检验分配是否成功
    if (null == p)
    {
        printf("malloc error.\n");
        return -1;
    }
    
    // 第三步:使用申请到的内存
    //p = null;
    //p = &a;    // 如果在free之前给p另外赋值,那么malloc申请的那段内存就丢失掉了
                // malloc后p和返回的内存相绑定,p是那段内存在当前进程的唯一联系人
                // 如果p没有free之前就丢了,那么这段内存就永远丢了。丢了的概念就是
                // 在操作系统的堆管理器中这段内存是当前进程拿着的,但是你也用不了
                // 所以你想申请新的内存来替换使用,这就叫程序“吃内存”,学名叫内存泄漏
    *(p+0) = 1;
    *(p+1) = 2;
    printf("*(p+0) = %d.\n", *(p+0));
    printf("*(p+1) = %d.\n", *(p+1));                
                    
    *(p+222) = 133;
    *(p+223) = 222;                
                    
    
    // 第四步:释放
    free(p);
    p = null;
    
    printf("*(p+222) = %d.\n", *(p+222));
    printf("*(p+223) = %d.\n", *(p+223));
*/
    return 0;
}

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

堆的结构信息

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    int *pint = (int *)malloc(sizeof(int[4]));

    memset(pint, 0, sizeof(int[4]));

    for (int i = 0; i < 4; i++)
    {
        pint[i] = i;
    }

    system("pause");
    return 0;
}

[笔记]堆和规范的堆操作 //规范的动态资源操作

 

 运行后如上图,返回值0x007bcbb8 指向的是堆的正文

往上数两行(0x20)

0x007bcb98  :堆块的起始位置,此处用四个字节保存着当前堆之前申请的堆的起始位置(为0则表示当前堆为第一个申请的堆)

0x007bcb9c: 下一堆块的起始地址 (00 没有下一个堆块)

0x007bcba0:表示申请堆所在源码的全路径(指针) 00 表示没有加调试信息

0x007bcba4:申请堆所在源码对应的行数。

0x007bcba8:使用堆空间大小,malloc 的参数 

0x007bcbac:

0x007bcbb0:

 

0x007bcbb4:上溢标志   标志位

0x007bcbc8:下溢标志, 标志位

 

规范的堆操作 //规范的动态资源操作

运行时才调入的资源叫做动态资源, malloc free  动态申请函数

int *pint = null;   (malloc 错误时候返回null)

       // 1.资源的引用变量初始化为无效(或者错误)值,

//2.申请资源后必须按照正确的方式去检查申请结果是否失败,

      if (pint == null)

{

//3.如果申请失败,流程转移到错误处理和释放资源处。

errorproc();

goto exit_proc;  //释放资源

}

//4.流程到达这里说明资源申请成功,现在可以开始使用资源了。

exit_proc:

//5.释放资源时,先检查资源的状态是否有效

if(pint  !=  null)

{

//6.按正确方法释放资源

free(pint);

//7.将资源的引用变量重置为无效(或者错误)状态

pint = null;

} 

 

后申请的先释放,先申请的后释放

 

d参考资料:

1.系统内核之堆管理:

2.c语言中内存分配:

3.浅谈c语言中的堆:

4.程序员的自我修养十内存:https://www.cnblogs.com/tan-sir/p/7652149.html