zoukankan      html  css  js  c++  java
  • malloc()和free()的相关知识

    之前有一篇文章是写全局变量、静态变量、局部变量、静态局部变量以及栈和堆在内存中的存储区别的,最近我又看了一篇关于C中Malloc函数和 Free函数对内存操作的,《浅谈C中的malloc和free》,通过这篇文章,我对C的内存申请以及释放有了新的、全面的认识。我想,不仅是 C,C++或者C#等其他语言的内存申请释放应该也是使用相近的方法吧。下面我简单总结了一下《浅谈》的主要内容,再加上一点我自己的理解。

    之前,我对malloc和free的了解,仅仅是在C中使用这两个函数来申请和释放内存,只知道怎么使用,用malloc申请一段给定大小的内存,返回一个Void* 指针,当使用完这段内存后需要手动调用free来释放这段内存。

    一、malloc() 和 free() 的基本概念和用法:

    1、函数原型及说明:

    void *malloc(long NumBytes): 该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。

    void free(void *FirstBytes): 该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。

    2、函数用法:

    这两个函数的用法其实很简单,就是用malloc()申请内存,用完这段内存后交给free()来释放着段内存。简单例子:

    malloc和free的示例程序
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    #include<stdio.h>
    int main()
    {
        char *Ptr = NULL;
        Ptr = (char*)malloc(100 * sizeof(char)); //Malloc
        if (NULL == Ptr)
        {
            printf("malloc failed");
            return 1;
        }
        gets(Ptr);
        printf("%s",Ptr);
        //code...
     
        free(Ptr);//Free
        return 0;
    }

    这是一个非常简单的情况,实际情况要具体问题具体分析,比如说你有一个函数专门负责申请内存,然后将地址指针返回给调用者,那么这段内存的释放就应该交给别人~

    3、函数使用的注意事项:

    A、malloc()调用后必须检查返回值,检查是否申请成功,malloc()申请失败则返回NULL

    B、当申请的内存不再被使用后,要free释放内存,并将变量指针赋值为NULL,防止后面的程序不小心使用了他。

    C、malloc()和free()是配对使用的,申请后不释放就是内存泄漏,如果释放多次则会引起错误(释放空指针除外,空指针可以释放多次)。

    D、malloc()的返回值是Void* 类型的,可以赋值给任何指针,但是最好还是在前面使用强制类型转换,这样可以躲过一些编译器的检查,少点Warning还是不错的:)。

    二、malloc做了什么?

    1、malloc()从哪里得到的内存空间?

    答案是从堆里得到的,函数返回值是指向堆里一块内存的指针。OS中有一个记录空闲内存地址的链表。当OS收到内存申请的时候,就会遍历该链表,寻找 第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。当然,具体的内存分配由OS的不同而略有不同。

    2、什么是堆,什么是栈?

    对于这个问题,我在之前的文章中有过解释,但是感觉没有《浅谈》作者解释的更透彻,故引用其原文于此以作补充:

    什么是:堆是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。

    什么是:栈是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独 立。每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言 里面显式的分配和释放。

    栈是由编译器自动分配释放,存放函数的参数值、局部变量的值等。操作方式类似于数据结构中的栈。

    堆一般由程序员分配释放,若不释放,程序结束时可能由OS回收。注意这里说是可能,并非一定。所以我想再强调一次,记得要释放!

    但是需要注意的是,如果你在函数里面定义了一个指针变量,然后在函数中申请了一块内存让指针指向它。分析一下现在的情况,这个指针变量是存在于上面的,但是这个指针变量所指向的内存是在上面的。这是重点,你要明白,在函数执行完之后,这个栈上的指针变量被释放了,但是它所指向的堆上的内存并没有被释放!

    1
    2
    3
    4
    void Function(void)
    {
        char *Ptr = (char*)malloc(100 * sizeof(char));
    }

    这个例子中,千万不要因为函数执行完并返回了,函数所在的栈被销毁了,Ptr指针被销毁了,就以为malloc申请的这段内存也被释放了。这是错误的,申请的内存在堆上,和这个栈没有一毛钱的关系。记住一定要释放!

    3、free()释放了什么

    free()其实很简单,就是释放了指针指向的内存,注意,是内存而不是指针,指针本身并没有被改变,仅仅是指针所指向的内存现在变成了未定义的不 可用的内存。所以,请在free了指针所指向的内存后,手动把指针指向NULL,防止后面的代码又去操作那段已经被释放的内存,不然的话会产生不可预计的 严重错误。

    三、malloc()和free()的机制

    这一部分是重点中的重点,之上的部分,虽然以前不是特别明白,但是多少还是有些了解的。但是下面这些,以前真的是一点都不知道,现在有了这些了解,我对C语言的内存申请机制,有了很深的了解,终于明白是怎么回事了!这是转折性的认识。

    通过上面free()的原型,我们可以看到,free()非常简单,只有一个指针参数,他是怎么知道要释放多少内存的呢?谁告诉它的呢?解释这个问 题,我们要先了解malloc()的申请。申请一段内存的时候,实际上申请到的内存要比你申请的要大一些,多出来的这部分内存就是用来记录对这块内存的管 理信息的。引用《UNIX环境高级编程》中第七章的一段话:

    大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度,指向下一个分配块的指针 等等。这就意味着如果写过一个已分配区的尾端,则会改写后一块的管理信息。这种类型的错误是灾难性的,但是因为这种错误不会很快就暴露出来,所以也就很难 发现。将指向分配块的指针向后移动也可能会改写本块的管理信息。

    malloc()申请到的内存实际上包含两部分,第一部分是一个记录管理信息的空间,然后才是可用空间。这部分记录管理信息的空间实际上保存着一个结构体,mem_control_block。

    mem_control_block原型
    1
    2
    3
    4
    struct men_control_block{
        int is_available;   //一个标记符
        int size;           //这是实际空间大小
    };

    这里size就是实际空间的大小,malloc()申请得到的指针指向了一段size大小的内存,这个指针指向了实际可用的内存的第一个字节,在这个地址之前的就是这个mem_control_block结构。通过free()的源码,我们将更加容易理解。

    1
    2
    3
    4
    5
    6
    7
    void free(void *ptr)
    {
        struct mem_control_block *free;
        free = ptr - sizeof(struct mem_control_block); //这是重点
        free->is_available = 1;
        return;
    }

    第二句是关键,它把指向可用空间的指针倒回去了一个mem_control_block大小,这样free指针就指向了这块内存的管理信息的那块空 间。后面那句free->is_available = 1;就是将这块内存交还给系统,具体的释放操作由OS来执行,这个is_available标志符的意思大概就是通知OS紧跟着的size个字节的内存空 间是可用空间了,可以被OS来重新分配了。这就是is_available的作用,它标志着这段内存是否可被重新分配。由此也可看出,具体的释放操作不是 由free()函数来执行的而是交给了操作系统。

    让我们再仔细观察第二句,free是直接从ptr减去一个结构的大小得到的,换句话说就是ptr必须指向实际可用空间的第一个字节,也就是 说,free()的参数必须直接是malloc()的返回值,不能对这个指针进行移动操作,不然,减去结构体大小之后一定不是指向管理信息空间的首地址。 不信的话你可以自己写一个程序然后移动指向可用空间的指针,看程序会有会崩!

    本文很多内容引用自http://bbs.bccn.net/thread-81781-1-1.html

  • 相关阅读:
    Java_JAVA6动态编译的问题
    Java_动态加载类(英文)
    Java_Java Compiler 应用实例
    Java_关于App class loader的总结
    Java_动态加载
    Java_Java SE6调用动态编译
    python捕获Ctrl+C信号
    python使用协程并发
    python使用多进程
    python使用多线程
  • 原文地址:https://www.cnblogs.com/zhanglanyun/p/2334213.html
Copyright © 2011-2022 走看看