【C/C++内存分布】

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

    目录

    1. C/C++内存分布

     2. C语言中动态内存管理方式malloc/calloc/realloc/free

     3. C++内存管理方式

     3.1 new/delete操作内置类型

     3.2 new和delete操作自定义类型

    4. operator new与operator delete函数重要

    5. new和delete的实现原理

    5.1 内置类型

    5.2 自定义类型

     6. 定位new表达式(placement-new) 了解

    7. 常见面试题

    7.1 malloc/free和new/delete的区别

    7.2 内存泄漏

     7.2.2 内存泄漏分类了解

     7.2.3 如何检测内存泄漏了解

     7.2.4如何避免内存泄漏

     8 总结


    1. C/C++内存分布

    我们先来看下面的一段代码和相关问题

    int globalVar = 1;
    static int staticGlobalVar = 1;
    void Test()
    {
     static int staticVar = 1;
     int localVar = 1;
     int num1[10] = { 1, 2, 3, 4 };
     char char2[] = "abcd";
     const char* pChar3 = "abcd";
     int* ptr1 = (int*)malloc(sizeof(int) * 4);
     int* ptr2 = (int*)calloc(4, sizeof(int));
     int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
     free(ptr1);
     free(ptr3);
    }
    1. 选择题
      选项 : A .   B .   C . 数据段 ( 静态区 )   D . 代码段 ( 常量区 )
      globalVar 在哪里 ____   staticGlobalVar 在哪里 ____
      staticVar 在哪里 ____   localVar 在哪里 ____
      num1 在哪里 ____
      char2 在哪里 ____   * char2 在哪里 ___
      pChar3 在哪里 ____       * pChar3 在哪里 ____
      ptr1 在哪里 ____         * ptr1 在哪里 ____
    2. 填空题
      sizeof ( num1 ) = ____ ;
      sizeof ( char2 ) = ____ ;       strlen ( char2 ) = ____ ;
      sizeof ( pChar3 ) = ____ ;     strlen ( pChar3 ) = ____ ;
      sizeof ( ptr1 ) = ____ ;
    3. sizeof strlen 区别

     大家可以自己先做一下

    我相信大多数题都难不倒大家但是有几个题大家做的时候一定要小心这里我把参考答案给大家红色表示容易出错的地方

    1. 选择题
      选项 : A .   B .   C . 数据段 ( 静态区 )   D . 代码段 ( 常量区 )
      globalVar 在哪里 __C__   staticGlobalVar 在哪里 __C__
      staticVar 在哪里 __C__   localVar 在哪里 __A__
      num1 在哪里 __A__
      char2 在哪里 __A__   * char2 在哪里 _A__
      pChar3 在哪里 __A__       * pChar3 在哪里 __ D_ _
      ptr1 在哪里 __A__         * ptr1 在哪里 __B__
    2. 填空题
      sizeof ( num1 ) = _40___ ;
      sizeof ( char2 ) = __5__ ;       strlen ( char2 ) = __4__ ;
      sizeof ( pChar3 ) = __ 4 __ ;     strlen ( pChar3 ) = _4___ ;
      sizeof ( ptr1 ) = __4__ ;
    3. sizeof strlen 区别
    sizeof和strlen再求解字符串类型时sizeof要包括'\0',strlen不包括'\0'。
    sizeof(数组名)表示整个数组大小strlen则不是。

    【说明】
    1. 又叫堆栈 -- 非静态局部变量 / 函数参数 / 返回值等等栈是向下增长的。
    2. 内存映射段 是高效的 I/O 映射方式用于装载一个共享的动态内存库。用户可使用系统接口
    创建共享共享内存做进程间通信。Linux 课程如果没学到这块现在只需要了解一下
    3. 用于程序运行时动态内存分配堆是可以上增长的。
    4. 数据段 -- 存储全局数据和静态数据。
    5. 代码段 -- 可执行的代码 / 只读常量。

     2. C语言中动态内存管理方式malloc/calloc/realloc/free

    void Test ()
    {
    int* p1 = (int*) malloc(sizeof(int));
    free(p1);
    // 1.malloc/calloc/realloc的区别是什么
    int* p2 = (int*)calloc(4, sizeof (int));
    int* p3 = (int*)realloc(p2, sizeof(int)*10);
    // 这里需要free(p2)吗
    free(p3 );
    }
    【面试题】
    1. malloc/calloc/realloc 的区别
    2. malloc 的实现原理
    回答
    1  malloc只负责向堆上申请空间calloc/realloc可以在原先申请空间上进行扩容有原地扩容以及异地扩容两种方式但是realloc不会对申请的空间初始化calloc会将申请的空间全部都置为0.
    2  大家可以参照这个视频 glibc中malloc实现原理

     3. C++内存管理方式

    C 语言内存管理方式在 C++ 中可以继续使用但有些地方就无能为力而且使用起来比较麻烦因此 C++ 又提出了自己的内存管理方式 通过 new delete 操作符进行动态内存管理

     3.1 new/delete操作内置类型

    void Test()
    {
      // 动态申请一个int类型的空间
      int* ptr4 = new int;
      
      // 动态申请一个int类型的空间并初始化为10
      int* ptr5 = new int(10);
      
      // 动态申请10个int类型的空间
      int* ptr6 = new int[3];
      delete ptr4;
      delete ptr5;
      delete[] ptr6;
    }
    注意申请和释放单个元素的空间使用 new delete 操作符申请和释放连续的空间使用new[] delete[] 注意匹配起来使用。

     3.2 newdelete操作自定义类型

    class A
    {
    public:
     A(int a = 0)
     : _a(a)
     {
     cout << "A():" << this << endl;
     }
     ~A()
     {
     cout << "~A():" << this << endl;
     }
    private:
     int _a;
    };
    int main()
    {
     // new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
    还会调用构造函数和析构函数
     A* p1 = (A*)malloc(sizeof(A));
     A* p2 = new A(1);
     free(p1);
     delete p2;
     // 内置类型是几乎是一样的
     int* p3 = (int*)malloc(sizeof(int)); // C
     int* p4 = new int;
     free(p3);
     delete p4;
     cout<<endl;
     A* p5 = (A*)malloc(sizeof(A)*3);
     A* p6 = new A[3];
     free(p5);
     delete[] p6;
     return 0;
    }

     运行结果

    注意在申请自定义类型的空间时 new 会调用构造函数 delete 会调用析构函数而 malloc 与free 不会

    4. operator newoperator delete函数重要

    new delete 是用户进行 动态内存申请和释放的操作符 operator new operator delete
    系统提供的 全局函数 new 在底层调用 operator new 全局函数来申请空间 delete 在底层通过operator delete 全局函数来释放空间。

    /*
    operator new该函数实际通过malloc来申请空间当malloc申请空间成功时直接返回申请空间
    失败尝试执行空               间不足应对措施如果改应对措施用户设置了则继续申请否
    则抛异常。
    */
    void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
    {
    // try to allocate size bytes
    void *p;
    while ((p = malloc(size)) == 0)
     if (_callnewh(size) == 0)
         {
             // report no memory
             // 如果申请内存失败了这里会抛出bad_alloc 类型异常
             static const std::bad_alloc nomem;
             _RAISE(nomem);
         }
    return (p);
    }
    /*
    operator delete: 该函数最终是通过free来释放空间的
    */
    void operator delete(void *pUserData)
    {
         _CrtMemBlockHeader * pHead;
         RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
         if (pUserData == NULL)
             return;
         _mlock(_HEAP_LOCK);  /* block other threads */
         __TRY
             /* get a pointer to memory block header */
             pHead = pHdr(pUserData);
              /* verify block type */
             _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
             _free_dbg( pUserData, pHead->nBlockUse );
         __FINALLY
             _munlock(_HEAP_LOCK);  /* release other threads */
         __END_TRY_FINALLY
         return;
    }
    /*
    free的实现
    */
    #define   free(p)               _free_dbg(p, _NORMAL_BLOCK)
    通过上述两个全局函数的实现知道 operator new 实际也是通过 malloc 来申请空间 如果
    malloc 申请空间成功就直接返回否则执行用户提供的空间不足应对措施如果用户提供该措施就继续申请否则就抛异常。 operator delete 最终是通过 free 来释放空间的

     5. newdelete的实现原理

    5.1 内置类型

    如果申请的是内置类型的空间 new malloc delete free 基本类似不同的地方是
    new/delete 申请和释放的是单个元素的空间 new[] delete[] 申请的是连续空间而且 new 在申请空间失败时会抛异常 malloc 会返回 NULL

    5.2 自定义类型

    new 的原理
    1. 调用 operator new 函数申请空间
    2. 在申请的空间上执行构造函数完成对象的构造
    delete 的原理
    1. 在空间上执行析构函数完成对象中资源的清理工作
    2. 调用 operator delete 函数释放对象的空间
    new T[N] 的原理
    1. 调用 operator new[] 函数在 operator new[] 中实际调用 operator new 函数完成 N 个对
    象空间的申请
    2. 在申请的空间上执行 N 次构造函数
    delete[] 的原理
    1. 在释放的对象空间上执行 N 次析构函数完成 N 个对象中资源的清理
    2. 调用 operator delete[] 释放空间实际在 operator delete[] 中调用 operator delete 来释
    放空间

     6. 定位new表达式(placement-new) 了解

    定位 new 表达式是在 已分配的原始内存空间中调用构造函数初始化一个对象
    使用格式
    new (place_address) type或者new (place_address) type(initializer-list)
    place_address 必须是一个指针 initializer-list 是类型的初始化列表 。
    使用场景
    定位 new 表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化所以如
    果是自定义类型的对象需要使用 new 的定义表达式进行显示调构造函数进行初始化。
    class A
    {
    public:
     A(int a = 0)
     : _a(a)
     {
     cout << "A():" << this << endl;
     }
     ~A()
     {
     cout << "~A():" << this << endl;
     }
    private:
     int _a;
    };
    // 定位new/replacement new
    int main()
    {
     // p1现在指向的只不过是与A对象相同大小的一段空间还不能算是一个对象因为构造函数没
    有执行
     A* p1 = (A*)malloc(sizeof(A));
     new(p1)A;  // 注意如果A类的构造函数有参数时此处需要传参
     p1->~A();
     free(p1);
     A* p2 = (A*)operator new(sizeof(A));
     new(p2)A(10);
     p2->~A();
     operator delete(p2);
      return 0;
    }


    7. 常见面试题

    7.1 malloc/freenew/delete的区别

    malloc/free new/delete 的共同点是都是从堆上申请空间并且需要用户手动释放。不同的地方是
    1. malloc free 是函数 new delete 是操作符 。
    2. malloc 申请的空间不会初始化 new 可以初始化 。
    3. malloc 申请空间时需要手动计算空间大小并传递 new 只需在其后跟上空间的类型即可如果是多个对象 [] 中指定对象个数即可 。
    4. malloc 的返回值为 void*, 在使用时必须强转 new 不需要因为 new 后跟的是空间的类型
    5. malloc 申请空间失败时返回的是 NULL 因此使用时必须判空 new 不需要但是 new
    要捕获异常 。
    6. 申请自定义类型对象时 malloc/free 只会开辟空间不会调用构造函数与析构函数而 new在申请空间后会调用构造函数完成对象的初始化 delete 在释放空间前会调用析构函数完成 空间中资源的清理。

     这些大家不必死记硬背理解性的记忆就好了。

    7.2 内存泄漏

    什么是内存泄漏内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内 存泄漏并不是指内存在物理上的消失而是应用程序分配某段内存后因为设计错误失去了对 该段内存的控制因而造成了内存的浪费。
    内存泄漏的危害长期运行的程序出现内存泄漏影响很大如操作系统、后台服务等等出现
    内存泄漏会导致响应越来越慢最终卡死。
    void MemoryLeaks()
    {
       // 1.内存申请了忘记释放
      int* p1 = (int*)malloc(sizeof(int));
      int* p2 = new int;
      
      // 2.异常安全问题
      int* p3 = new int[10];
      
      Func(); // 这里Func函数抛异常导致 delete[] p3未执行p3没被释放.
      
      delete[] p3;
    }

     7.2.2 内存泄漏分类了解

    C/C++程序中一般我们关心两种方面的内存泄漏

    • 堆内存泄漏(Heap leak) 堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放那么以后这部分空间将无法再被使用就会产生Heap Leak
    • 系统资源泄漏指程序使用系统分配的资源比方套接字、文件描述符、管道等没有使用对应的函数释放掉导致系统资源的浪费严重可导致系统效能减少系统执行不稳定。

     7.2.3 如何检测内存泄漏了解

    vs 下可以使用 windows 操作系统提供的 _CrtDumpMemoryLeaks() 函数进行简单检测该
    函数只报出了大概泄漏了多少个字节没有其他更准确的位置信息。
    int main()
    {
     int* p = new int[10];
     // 将该函数放在main函数之后每次程序退出的时候就会检测是否存在内存泄漏
     _CrtDumpMemoryLeaks();
     return 0;
    }
    
    // 程序退出后在输出窗口中可以检测到泄漏了多少字节但是没有具体的位置
    Detected memory leaks!
    Dumping objects ->
    {79} normal block at 0x00EC5FB8, 40 bytes long.
    Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
    Object dump complete.
    因此写代码时一定要小心尤其是动态内存操作时一定要记着释放。但有些情况下总是防不胜
    防简单的可以采用上述方式快速定位下。如果工程比较大内存泄漏位置比较多不太好查时
    一般都是借助第三方内存泄漏检测工具处理的。

     7.2.4如何避免内存泄漏

    1. 工程前期良好的设计规范养成良好的编码规范申请的内存空间记着匹配的去释放。 ps 这个理想状态。但是如果碰上异常时就算注意释放了还是可能会出问题。需要下一条智 能指针来管理才有保证。
    2. 采用 RAII 思想或者智能指针来管理资源。
    3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
    4. 出问题了使用内存泄漏工具检测。 ps 不过很多工具都不够靠谱或者收费昂贵。

     总结一下:

    内存泄漏非常常见解决方案分为两种 1 、事前预防型。如智能指针等。 2 、事后查错型。如泄漏检测工具。

     8 总结

    本篇博客重点讲解了C/C++动态内存管理的不同以及其他一些关于new/delete操作符的讲解还列举了一些常考的面试题如果该文对你有帮助的话能不能3连支持一下博主呢

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
    标签: c++

    “【C/C++内存分布】” 的相关文章