• 深剖malloc、new


    如果问你:malloc和new有什么区别?讲讲malloc,越多越好?

      malloc 和 new都是基于堆上开辟出对应的空间,这段空间除非进程结束不然不会释放,所以分别需要free 和 delete来释放。

      new 还可以用 new[] 来开辟一段连续的空间,new和malloc不同,new在为一个类的对象开辟空间时还会调用对象的构造函数。

      同理,delete 和 delete[] 在针对一个类的对象的空间时也会调用对应的析构函数,而malloc 和 free不会。

    这样够吗????

    够不够我不知道,我觉得,能回答的更好:

    一.他们不是一类东西

      new 是C++规则里给的一个关键字,它是一个运算符,可以被重载,而malloc 是一个函数,它的目的就是在堆上开辟一块空间。

      new 的底层也是根据malloc 实现的,如果你有VS,完全可以调试时按F11 进new 过程看看。

      可以发现,new 和 delete 一个NULL对象,是允许的,进去以后会有个判断,判断到NULL会直接返回,不会报错。

      但是!!!!new 运算符是可以重载的,所以。它可以在堆以外的地方开辟空间吗???

      完全可以!

      因为本来new 底层是根据malloc 实现,行,我直接将其重载,让它在别的区域上起作用!这不就可以吗?

      所以,要纠正这个误区,在没被重载的情况下,new 是在堆上开辟空间。

    二.new是刘德华。

      我们经常可以看到,每次malloc 后,都要判断一下是否malloc 成功,如果不成功就不执行接下来的操作了。

      就比如如下代码:

    1 char *p = (char *)malloc(sizeof(char) * 20);
    2 if(NULL == p)
    3     perror("malloc"),exit(1);

       这是一个很好的习惯,在意外情况发生时能及时处理。

      但是,我们却不常常在new 的后面见到意外情况的处理。

      为什么?new 不会失败吗?

      因为new 失败后,会直接报错,编译器会自动终止你的进程。

      一个回不了头,一个还能挽救。

      当然,如果要让new 在失败以后也是返回NULL,就需要特殊的处理方法:

        1.在new 后面加上nothrow ,就比如用new(nothrow) A ,而不是new A 。

         2.重载new 运算符,千万别忘了我们C++ 重要的特性之一——多态。

      需要特别注明:每种编译器下new 的底层实现是不同的,所以返回NULL 还是报异常,都要看其底层的具体实现,所以,在使用一个编译器下的某种不确定的函数或关键字时,对底层的探索,重中之重。

    三.new[ ] 是有区别对待的

      new[ ] 一个有构造函数和析构函数的类的对象的数组时,new[ ] 开辟出的空间是要多开辟四个字节的区域在头部,用于记录对象个数。

      但是,当new[ ] 的空间是一块基本类型的数组,或者是一个不带有析构函数的类的对象数组时,这块区域就不会出现。

      让我们用代码探索一下:(这里我使用VS2013编辑代码)

      

     1 #include <iostream>
     2 
     3 using namespace std;
     4 
     5 class Test_1 //构造+析构
     6 {
     7 public:
     8     Test_1()
     9         :mem(1)
    10     {}
    11 
    12     ~Test_1()
    13     {
    14         cout << "Test_1" << endl;
    15     }
    16 private:
    17     int mem;
    18 };
    19 
    20 class Test_2 //无析构
    21 {
    22 public:
    23     Test_2()
    24         :mem(2)
    25     {}
    26 private:
    27     int mem;
    28 };
    29 
    30 int main()
    31 {
    32     Test_1 *p1 = new Test_1[10];
    33     Test_2 *p2 = new Test_2[10];
    34 
    35     int *p3 = new int[10];
    36     system("pause");
    37     return 0;
    38 }

      

      我设立了一个带析构的类Test_1和一个不带析构的类Test_2,我们再看new 的底层实现:

    1 void *__CRTDECL operator new[](size_t count) _THROW1(std::bad_alloc)
    2     {    // try to allocate count bytes for an array
    3     return (operator new(count));
    4     }

      

      可见new[ ] 底层调用了函数operator new ,并赋予一个参数count,用于记录开辟的空间大小。

      让我们看一下对于3次调用new的count大小:

    Test_1:

    Test_2:

    int:

     

      由此可见,对于带析构函数的类,new[ ]在开辟空间时会多开辟4个字节的空间,用于什么呢?我们来看一下内存。

        

      可以很明显看到,p1指针所在位置前一个地址的值为0x0000000a,化为10进制值为10,可知其就是为了保存所需要开辟的对象有多少个。

      可以总结出:这里特地腾出的空间是专门为类的析构函数服务的,每次开辟空间的指针在释放空间时,都会根据空间前四个字节内的值来得出——是否需要调用析构函数?以及,调用多少次析构函数?

      

    四.malloc所开辟的,可不止这么点点。

      有没有想过?malloc 开辟一块堆上空间后,系统怎么去管理它?下次需要开辟空间时,系统怎么知道这块区域已被用?

      没事,系统总会有办法的。

      malloc 的作用,不是去堆上直接取,而是向操作系统申请这块空间的使用权,等CPU响应这个申请,然后执行相应的操作,当然,操作系统为了更好地管理malloc 完的这块空间,它会选择给这块空间之前加一个结构体,也就是一个装有这块空间的属性的包,在这块空间后一位的地址处内容是0xFCFCFCFC,可以理解为是这块空间的终止符,让系统能分辨出这块空间申请到何处。

      malloc 有关的结构体:

    1 typedef struct s_block *t_block;
    2 struct s_block {
    3 size_t size; /* 数据区大小 */
    4 t_block next; /* 指向下个块的指针 */
    5 int free; /* 是否是空闲块 */
    6 int padding; /* 填充4字节,保证meta块长度为8的倍数 */
    7 char data[1] /* 这是一个虚拟字段,表示数据块的第一个字节,长度不应计入meta */
    8 };

      系统会根据当前操作系统空间块的分配算法,分配给malloc 合适的空间并附上结构体和收尾标识以助于管理。

      所以,malloc 出来的空间会大于用户可见的空间。

  • 相关阅读:
    《如何快速阅读一本书》读书笔记总结(实测,对提升阅读速度和质量非常有用)---2020年的第8/100本 (一目十行读书训练法)
    穷查理宝典 读书笔记--2020年的第1/100本
    200个查理芒格思维模型---多元思维模型
    lintcode算法周竞赛
    2016 Google code jam 大赛
    程序员面试心得总结
    (lintcode全部题目解答之)(附容易犯的错误)
    模板
    《非连续性》 混沌大学商学院第3章 读书笔记总结--2020年的第19/100本
    《第二曲线》 混沌大学商学院第2章 读书笔记总结--2020年的第18/100本
  • 原文地址:https://www.cnblogs.com/shy0322/p/8920743.html
Copyright © 2020-2023  润新知