C/C++内存管理

打印 上一主题 下一主题

主题 572|帖子 572|积分 1716

目次
1. C/C++内存分布 
 2. C语言中动态内存管理
3. C++内存管理方式
3.1 new/delete操作内置范例 
3.2 new和delete操作自定义范例 
4. operator new和operator delete函数 
4.1 operator new和operator delete函数 
5. new和delete的实现原理 
5.1 内置范例
5.2 自定义范例
6. 定位new表达式(placement-new)
7. malloc/free和new/delete的区别


1. C/C++内存分布 


   分析:
  

  • 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  • 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通讯。
  • 堆用于程序运行时动态内存分配,堆是向上增长的。
  • 数据段--存储全局数据和静态数据。
  • 代码段--可实行的代码/只读常量。 
  1. int globalvar = 1;
  2. static int staticGlobalvar = 1;
  3. void Test()
  4. {
  5.         static int staticvar = 1;
  6.         int localvar = 1;
  7.         int num1[10] = { 1,2,3,4 };
  8.         char char2[] = "abcd";
  9.         const char* pchar = "abcd";
  10.         int* ptr1 = (int*)malloc(sizeof(int) * 4);
  11.         int* ptr2 = (int*)calloc(4, sizeof(int));
  12.         int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
  13.         free(ptr1);
  14.         free(ptr3);
  15. }
复制代码
  1. /*
  2. globalvar在:数据段(静态区)
  3. staticGlobalvar在:数据段(静态区)
  4. staticvar在:数据段(静态区) - Test函数结束以后staticvar变量不销毁
  5. localvar在:栈
  6. num1在:栈
  7. char2在:栈 - char2和num1一样都是数组,数组名表示首元素的地址,
  8. 存的是地址,在栈上
  9. *char2在:栈 - char2在进行运算的时候代表整个数组,整个栈上的数组
  10. pchar3在:栈 - 局部的指针,也是在栈上。
  11. *pchar3在:代码段(常量区) - pchar3指向的是首元素的地址,
  12. 解引用得到首元素,而这个'a'就放在代码段(常量区)
  13. ptr1在:栈 - 局部的指针,在栈上
  14. *ptr1在:堆 - 解引用指向动态开辟的空间,在堆上
  15. */
复制代码

 2. C语言中动态内存管理

1. malloc/calloc/realloc的区别是什么?
malloc实用于分配未初始化的内存块,calloc实用于分配并初始化的内存块,realloc实用于调整已有内存块的巨细。
2. malloc的实现原理。
【CTF】GLibc堆利用入门-机制先容_哔哩哔哩_bilibiliCTF Pwn中堆相干机制的入门讲解。内容比力浅,讲得也比力快,如果发现什么错误还望指正。, 视频播放量 6376、弹幕量 12、点赞数 170、投硬币枚数 151、收藏人数 331、转发人数 41, 视频作者 bili53448916889, 作者简介 开学了,大概会更得慢一点?,相干视频:【CTF】GLibc堆利用-Double Free,【CTF Pwn】GLibc堆利用-Off-By-One,【CTF】CTF题型简介,【网络安全】CTF夺旗赛新手入门到进阶实操,含历届真题详解!,PWN入门教程-工具讲解、栈溢出漏洞简朴利用 网络安全CTF比赛 信息安全管理与评估 网络与信息安全管理员,B站最好的绿盟科技CTF夺旗赛教程 从小白入门到比赛实战,2023羊城杯pwn_01(risky_login),【暗网黑客教程】审核下架34次,终于上传成功,你敢学我就敢发,学不会我来教~(暗網怎么进/暗網教程/怎样上暗網/手機怎样上暗網),【全网最全讲解】1分钟带你了解什么是CTF?,【CTF-杂项】task_图片隐写sub
https://www.bilibili.com/video/BV117411w7o2/?spm_id_from=333.788.videocard.0

3. C++内存管理方式

   C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比力贫苦,因此C++又提出了自己的内存管理方式:通过new和deletc操作符进举措态内存管理。
  3.1 new/delete操作内置范例 

  1. int main()
  2. {
  3.         //动态申请一个int类型的空间
  4.         int* ptr4 = new int;
  5.         //动态申请一个int类型的空间并初始化为10
  6.         int* ptr5 = new int(10);
  7.         //动态申请10个int类型的空间
  8.         int* ptr6 = new int[3];
  9.         //动态申请10个int类型的空间并初始化 - 跟数组的初始化是一样的
  10.         int* ptr7 = new int[3]{ 1,2,3 };
  11.         int* ptr8 = new int[5]{ 1,2,3 };
  12.         //释放
  13.         delete ptr4;
  14.         delete ptr5;
  15.         delete[] ptr6;
  16.         delete[] ptr7;
  17.         delete[] ptr8;
  18.         return 0;
  19. }
复制代码

   申请和释放单个元素的空间,使用new和deletc操作符,申请和释放连续的空间,使用new[]和delete[],匹配起来使用。 
  3.2 new和delete操作自定义范例 

  1. #include <iostream>
  2. using namespace std;
  3. class A
  4. {
  5. public:
  6.         A(int a = 1)
  7.                 :_a(a)
  8.         {
  9.                 cout << "A():" << this << endl;
  10.         }
  11.         ~A()
  12.         {
  13.                 cout << "~A():" << this << endl;
  14.         }
  15. private:
  16.         int _a;
  17. };
  18. int main()
  19. {
  20.         //new/delete 和 malloc/free最大区别是 new/deeletc对于
  21.         //自定义类型除了开空间还会调用构造函数和析构函数。
  22.         A* p1 = (A*)malloc(sizeof(A));
  23.         A* p2 = new A;//没有默认构造就会报错,当然也可以自己传参
  24.         //A* p3 = new A(1);//这里有点像匿名对象,但不是匿名对象
  25.         //这是在堆上new一个对象,然后构造。然后把对象的地址返回给p3
  26.         free(p1);//释放空间
  27.         delete p2;//析构+释放空间 - 释放空间之前先调析构函数
  28.         //创建多个对象
  29.         A* p4 = (A*)malloc(sizeof(A) * 10);
  30.         A* p5 = new A[10];//new了10个A对象,那么就会调用10次构造函数
  31.         free(p4);
  32.         delete[] p5;//析构的时候会对这10个对象依次析构,然后释放内存空间
  33.         //创建多个对象并且初始化
  34.         //A aa1(1);
  35.         //A aa2(2);
  36.         //A* p6 = new A[10]{aa1,aa2};//调用有名对象,后面的调默认构
  37.         //造,没默认构造就报错
  38.         //也可以直接用匿名对象
  39.         //A* p6 = new A[10]{ A(1),A(2) };
  40.         //也可以用隐式类型转换
  41.         A* p6 = new A[10]{ 1,2 };//本质是构造临时对象,然后走拷贝构造,
  42.         //编译器优化成了直接构造也是一样的
  43.         delete[] p6;
  44.         return 0;
  45. }
复制代码

前两个传参,背面直接调用默认构造。 
 没有默认构造的话代码就会出问题,所以一个类最好提供默认构造,可以镌汰不须要的贫苦。
   new/deletc相比C语言的malloc/free正真的优势是针对自定义范例,自定义范例主要就是调用构造函数和析构函数,其次用法上面要方便一些,还有初始化上面也要方便一些,new多个对象的话就要用[ ],本质上就是调用多次构造和多次析构,
  例子:构造一个链表
  1. struct ListNode
  2. {
  3.         int _index;
  4.         ListNode* _next;
  5.         ListNode(int index = 1)
  6.                 :_index(index)
  7.                 ,_next(nullptr)
  8.         {}
  9. };
  10. int main()
  11. {
  12.         //new相当于开空间+调用ListNode的构造函数初始化
  13.         ListNode* n1 = new ListNode(1);
  14.         ListNode* n2 = new ListNode(2);
  15.         ListNode* n3 = new ListNode(3);
  16.         ListNode* n4 = new ListNode(4);
  17.         n1->_next = n2;
  18.         n2->_next = n3;
  19.         n3->_next = n4;
  20.         //释放
  21.         delete n1;
  22.         delete n2;
  23.         delete n3;
  24.         delete n4;
  25.         return 0;
  26. }
复制代码
   new/delete在内置范例用法上不需要强转,不需要计算巨细,会方便一些,本质上没有区别,自定义范例有区别,自定义范例new/deletc会调用构造和析构,如果是多个对象就会调用多次构造和析构,其他地方没啥区别。
  4. operator new和operator delete函数 

4.1 operator new和operator delete函数 

   new和delete是用户进举措态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,不是运算符重载,new在底层调用operator new全局函数来申请空间,deletc在底层通过operator delete全局函数来释放空间。 
  1. /*
  2. operator new:该函数实际通过malloc来申请空间,
  3. 当malloc申请空间成功时直接返回,申请空间失败,
  4. 尝试执行空间不足应对措施,如果该应对措施用户设置了,
  5. 则继续申请,否则抛异常。
  6. operator new本质是对malloc的一个封装
  7. 封装是因为malloc失败会返回0,而operaotr new失败之后会抛异常
  8. 异常是C++处理错误的一种方式
  9. */
  10. void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
  11. {
  12.         // try to allocate size bytes
  13.         void* p;
  14.         while ((p = malloc(size)) == 0)
  15.                 if (_callnewh(size) == 0)
  16.                 {
  17.                         // report no memory
  18.                         // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
  19.                         static const std::bad_alloc nomem;
  20.                         _RAISE(nomem);
  21.                 }
  22.         return (p);
  23. }
  24. /*
  25. operator delete:该函数最终是通过free来释放空间的
  26. operator delete封装的不是free,而是封装的一个_free_dbg
  27. 的一个函数,其实_free_dbg就是free,其实库里面的free是一个
  28. 宏函数,所以我们也可以理解为operator delete就是对free的封装。
  29. */
  30. void operator delete(void* pUserData)
  31. {
  32.         _CrtMemBlockHeader* pHead;
  33.         RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
  34.         if (pUserData == NULL)
  35.                 return;
  36.         _mlock(_HEAP_LOCK);  /* block other threads */
  37.         __TRY
  38.                 /* get a pointer to memory block header */
  39.                 pHead = pHdr(pUserData);
  40.         /* verify block type */
  41.         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
  42.         _free_dbg(pUserData, pHead->nBlockUse);
  43.         __FINALLY
  44.                 _munlock(_HEAP_LOCK);  /* release other threads */
  45.         __END_TRY_FINALLY
  46.                 return;
  47. }
  48. /*
  49. free的实现 - 底层也是调_free_dbg
  50. */
  51. #define   free(p)        _free_dbg(p, _NORMAL_BLOCK)
复制代码
   通过上述两个全局函数的实现直到,operator new现实也是通过malloc来申请空间的,如果malloc申请空间成功就直接返回空间的地点,否则实行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete最终是通过free来释放空间的。
  5. new和delete的实现原理 

5.1 内置范例

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

   

  • new的原理
  

  • 调用operator new函数申请空间
  • 在申请的空间上实行构造函数,完成对象的构造
  

  • delete的原理
  

  • 在空间上实行析构函数,完成对象中资源的清算工作
  • 调用operator delete函数释放对象的空间
  

   我们new一个对象最核心的代码就是这两句指令,new的底层的核心就是去调用两个函数,调用operator new申请空间,调用构造函数去初始化,没有new我们自己也可以做如许的动作,但是用new会方便一些。 
  

   delete最核心的代码也是调用两个函数,调用析构函数和operator delete去释放空间。 
   

  • new T[N]的原理
  

  • 调用operator new[]函数.在operator new[]中现实调用operator new函数完成N个对象空间的申请。operator[]函数实在就是对operator new函数的一个封装。
  • 在申请的我空间上实行N次构造函数
  

  • delete[]的原理
  

  • 在释放的对象空间上实行N次析构函数,完成N个对象中资源的清算。
  • 调用operator delete[]释放空间,现实在operator delete[]中调用operator delete来释放空间。operator delete[]实在就是对operator delete的封装。 
  C++中的抛异常: 
  1. #include <iostream>
  2. using namespace std;
  3. void Func()
  4. {
  5.         int* p1 = new int[1024 * 1024 * 100];//400MB
  6.         cout << p1 << endl;
  7.         int* p2 = new int[1024 * 1024 * 100];
  8.         cout << p2 << endl;
  9.         int* p3 = new int[1024 * 1024 * 100];
  10.         cout << p3 << endl;
  11.         int* p4 = new int[1024 * 1024 * 100];
  12.         cout << p4 << endl;
  13.         int* p5 = new int[1024 * 1024 * 100];
  14.         cout << p5 << endl;
  15.         //当申请p5的时候就会失败,因为没那么大内存了
  16.         //弹出的框就是抛异常了。
  17. }
  18. int main()
  19. {
  20.         Func();
  21.         return 0;
  22. }
复制代码

那么我们就可以使用try catch来捕获异常。 
  1. #include <iostream>
  2. using namespace std;
  3. void Func()
  4. {
  5.         int* p1 = new int[1024 * 1024 * 100];//400MB
  6.         cout << p1 << endl;
  7.         int* p2 = new int[1024 * 1024 * 100];
  8.         cout << p2 << endl;
  9.         int* p3 = new int[1024 * 1024 * 100];
  10.         cout << p3 << endl;
  11.         int* p4 = new int[1024 * 1024 * 100];
  12.         cout << p4 << endl;
  13.         int* p5 = new int[1024 * 1024 * 100];
  14.         cout << p5 << endl;
  15.         //当申请p5的时候就会失败,因为没那么大内存了
  16.         //弹出的框就是抛异常了。
  17. }
  18. int main()
  19. {
  20.         try
  21.         {
  22.                 Func();
  23.         }
  24.         catch (const exception& e)
  25.         {
  26.                 cout << e.what() << endl;
  27.         }
  28.         return 0;
  29. }
复制代码

   抛异常我们就捕获异常,然后代码直接跳到catch,背面的代码不再实行,这个就是抛异常的机制,这个异常是operator new抛出来的。
  new从底层的角度要转换成调用开operator new函数和构造函数,operator new封装一下malloc是由于malloc失败是返回0,不符合这里的需求,封装了一下调用operator new,失败的话就会抛异常,这就是库内里operator new封装malloc的意义。
    注意:这个代码要在x86下运行,x86是32位,x64是64位,64位的话虚拟进程地点空间就要大很多,由于32位的程序是2^32次方字节, 也就是4G,64位环境下虚拟进程地点空间就是2^64次方,要大很多,所以就不会申请空间失败。也就不会捕获异常。
  operator new我们是可以直接使用的 
  1. class A
  2. { };
  3. //operator new我们是可以直接使用的
  4. int main()
  5. {
  6.         //跟malloc区别是失败会抛异常,所以不用检查返回值
  7.         //但是没有调用构造函数
  8.         //new也一样,不用检查返回值
  9.         A* p1 = (A*)operator new(sizeof(A));
  10.         return 0;
  11. }
复制代码
6. 定位new表达式(placement-new)

   定位new表达式是在已分配的原始内存中调用构造函数初始化一个对象。
  使用格式:
  new (place_address) type大概new (place_address) type(initializer-list)
  place_address必须是一个指针,initializer-list是范例的初始化列表
  使用场景:
  定位new表达式在现实中一样平常是配合内存池使用。由于内存池分配出的内存没有初始化,所以如果是自定义范例的对象,需要使用new的定义表达式进行表现调构造函数进行初始化。
  1. #include <iostream>
  2. using namespace std;
  3. class A
  4. {
  5. public:
  6.         A(int a = 1)
  7.                 :_a(a)
  8.         {
  9.                 cout << "A():" << this << endl;
  10.         }
  11.         ~A()
  12.         {
  13.                 cout << "~A():" << this << endl;
  14.         }
  15. private:
  16.         int _a;
  17. };
  18. int main()
  19. {
  20.         //构造函数是没办法显示调用的
  21.         //也就是说new没办法拆分为operator new和调拷贝构造
  22.         A* ptr1 = (A*)operator new(sizeof(A));
  23.         //ptr1->A(1);//err
  24.         //相对这块显示调用构造函数的空间的用法叫做定位new
  25.         new(ptr1)A(101);//这个时候就会对p1指向的空间调用构造函数初始化
  26.         //析构函数支持显示调用
  27.         ptr1->~A();
  28.         operator delete(ptr1);
  29.         //上半部分就充当了new的功能,下半部分就充当
  30.         //了detete的功能,和new和delete达到的效果一摸一样
  31.         return 0;
  32. }
  33. /*
  34. 但是平时不会这样写,这个定位new和显示调用析构
  35. 有些地方为了提高效率申请内存的时候如果直接用new
  36. 的话底层就是调operator new,也就是说调malloc了,
  37. 这种方式效率比较低,那么就会写一个叫内存池的东西
  38. 那如果我的空间是从我的内存池出来的,那就开好了空间
  39. 初始化的话就要用定位new去初始化,析构就是显示调用
  40. 析构,然后释放内存池,
  41. */
复制代码
7. malloc/free和new/delete的区别

   malloc/free和new/delete的共同点是:都是从堆上申请空间,而且需要用户手动释放。不同的地方是:
  

  • malloc和free是函数,new和delete是操作符。
  • malloc申请的空间不会初始化,new可以初始化。
  • malloc申请空间时,需要手动计算空间巨细并传递,new只需在其背面跟上空间的范例即可,如果是多个对象,[ ]中指定对象个数即可。
  • malloc的返回值是void*,在使用的时候必须强转,new不需要,由于new背面跟的是空间的范例。
  • malloc申请空间时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常。
  • 申请自定义范例对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清算释放。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

西河刘卡车医

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表