『C++成长记』内存管理

这篇具有很好参考价值的文章主要介绍了『C++成长记』内存管理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

『C++成长记』内存管理,c++,java,开发语言

🔥博客主页:小王又困了

📚系列专栏:C++

🌟人之为学,不日近则日退

❤️感谢大家点赞👍收藏⭐评论✍️

『C++成长记』内存管理,c++,java,开发语言

目录

一、C/C++内存分布

二、内存管理方式

📒2.1C语言内存管理方式 

📒2.2C++内存管理方式 

🎀2.2.1new/delete操作内置类型

🎀2.2.1new和delete操作自定义类型

三、operator new与operator delete函数

四、new和delete的实现原理

📒4.1内置类型

📒4.2自定义类型

五、定位new表达式(placement-new)

📒5.1概念

📒5.2使用格式

📒5.3使用场景

六、malloc/free和new/delete的区别

 七、内存泄漏

📒7.1什么是内存泄漏

📒7.2内存泄漏的危害

📒7.3内存泄漏分类

📒7.4检测内存泄漏

📒7.5避免内存泄漏


一、C/C++内存分布

    在C和C++中,内存分布主要包括以下几个部分:

代码段(Code Segment):

  • 包含程序的机器代码,也称为文本段。
  • 是只读的,存储程序的执行代码。
  • 在程序运行时不可修改

数据段(Data Segment):

  • 包括初始化的全局变量静态变量
  • 在程序开始运行时分配,并在整个程序的执行周期内保持不变
  • 数据区(Data Section): 存储已经初始化的全局变量和静态变量。
  • BSS段(Block Started by Symbol): 存储未初始化的全局变量和静态变量。

堆(Heap):

  • 由程序员分配和释放的内存区域。
  • 动态分配的内存(例如使用newmalloc函数)存储在这里。
  • 堆的大小在程序运行时可以动态调整。

栈(Stack):

  • 存储函数的局部变量和函数调用的上下文信息。
  • 由编译器自动分配和释放。
  • 栈的大小在程序运行时是固定的。

堆栈区域之间的空间(Heap-Stack Gap):

  • 用于防止堆和栈之间的冲突。
  • 可以防止堆溢出覆盖栈数据或栈溢出覆盖堆数据。

『C++成长记』内存管理,c++,java,开发语言 小Tips:这些区域在内存中的布局可能因操作系统和编译器而异,但通常遵循相似的结构。在运行时,栈的地址通常位于较高的内存地址,而堆的地址通常位于较低的内存地址。代码段和数据段的位置取决于可执行文件的加载方式,通常在较高的内存地址。 

二、内存管理方式

📒2.1C语言内存管理方式 

    C语言提供了一些基本的内存管理函数和操作符,使程序能够有效地分配和释放内存。以下是C语言中常用的内存管理方式:

📝malloc() 和 free():

  • malloc()函数用于动态分配指定大小的内存空间。
  • free()函数用于释放先前由malloc()分配的内存空间。
  • 例子:
// 分配10个整数大小的内存空间
int *ptr = (int *)malloc(10 * sizeof(int));
if (ptr != NULL) 
{
    // 使用内存
    // ...
    // 释放内存
    free(ptr);
}

📝 calloc() 和 realloc():

  • calloc()函数用于分配指定数量和大小的内存空间,并将其初始化为零。
  • realloc()函数用于更改之前分配的内存块的大小。
  • 例子:
// 分配并初始化为0,10个整数大小的内存空间
int *ptr = (int *)calloc(10, sizeof(int));
if (ptr != NULL)
{
    // 使用内存
    // ...

    // 重新分配内存空间为20个整数大小
    ptr = (int *)realloc(ptr, 20 * sizeof(int));

    // 使用重新分配后的内存
    // ...

    // 释放内存
    free(ptr);
}

📝静态分配:

  • 静态分配是在编译时为变量分配内存空间。
  • 包括全局变量、静态变量和局部变量(在函数外部定义的变量是静态的)。
  • 内存分配和释放由编译器自动处理。

📝自动变量:

  • 在函数内部定义的变量通常是自动变量。
  • 自动变量的内存分配和释放由程序的执行控制流决定。

📝指针和动态内存分配:

  • 使用指针进行内存分配和释放是C语言中动态内存管理的重要部分。
  • 必须小心管理分配的内存,以避免内存泄漏和悬空指针问题。

 小Tips:C语言中的内存管理是相对低级的,程序员需要手动分配和释放内存。不正确的内存管理可能导致内存泄漏、悬空指针或者堆栈溢出等问题。因此,编写健壮和高效的代码需要仔细管理内存的分配和释放。

📒2.2C++内存管理方式 

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

🎀2.2.1new/delete操作内置类型

void Test()
{
  // 动态申请一个int类型的空间
  int* ptr1 = new int;
  
  // 动态申请一个int类型的空间并初始化为10
  int* ptr2 = new int(10);
  
  // 动态申请10个int类型的空间,不初始化
  int* ptr3 = new int[10];
  // 动态申请10个int类型的空间,并初始化前三个
  int* ptr4 = new int[10]{1,2,3};
  delete ptr1;
  delete ptr2;
  delete[] ptr3;
  delete[] ptr4;
}

小Tips:用new开辟空间是不会进行初始化的。如果我们想动态申请并初始化,类型后面要跟圆括号 () ,动态申请多个连续空间,类型后面跟的是方括号 [ ] ,如果要对这多个连续的空间初始化,可以在 [ ] 的后面跟 { }  { } 里面是初始化的数据,初始化的值不够,后面会默认初始化为0。申请和释放单个元素的空间,使用newdelete操作符,申请和释放连续的空间,使用new[ ]delete[ ]

🎀2.2.1new和delete操作自定义类型

class A
{
public:
    A(int a = 0)
        : _a(a)
    {
        cout << "A():" << this << endl;
    }

    ~A()
    {
        cout << "~A():" << this << endl;
    }
private:
    int _a;
};

int main()
{
    //通过new动态申请一块空间,存储A类型变量
    A* p1 = new A(1);
    delete p1;

    //通过new动态申请3个连续空间,存储3个A类型的数据
    A* p2 = new A[3];
    delete[] p2;
    return 0;
}

『C++成长记』内存管理,c++,java,开发语言

小Tips:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而mallocfree不会。如果自定义类型没有默认构造函数,我们在new的时候就需要人为的传参进行初始化,并且申请了几块空间就要初始化几个,不像动态申请的内置类型数组,可以只初始化前面一部分,后面默认是0。

A* p6 = new A[3]{ 1, 2, 3 };//通过隐式类型转换去初始化
A* p6 = new A[3]{ A(1), A(2), A(3) };//通过匿名对象去初始化

三、operator new与operator delete函数

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

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来释放空间的

小Tips:operator newoperator delete不是运算符重载。operator newoperator deletemallocfree在功能上相似,都是到堆上申请空间,然后释放空间,它们的区别在于,前者失败了会抛异常,而后者失败了是返回错误码,通过返回值来表示这一块有问题。

四、new和delete的实现原理

📒4.1内置类型

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

📒4.2自定义类型

📖new的原理

  1. 调用operator new函数申请空间。
  2. 在申请的空间上执行构造函数,完成对象的构造。

📖delete的原理

  1. 在空间上执行析构函数,完成对象中资源的清理工作。
  2. 调用operator delete函数释放对象的空间。

📖new T[N]的原理 

  1. 调用operatr new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请。
  2. 在申请的空间上执行N次构造函数。

📖delete[]的原理

  1.  在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理。
  2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间。

五、定位new表达式(placement-new)

📒5.1概念

    定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象

📒5.2使用格式

  • new (place_address) type
  • new (place_address) type(initializer-list)

place_address必须是一个指针,initializer-list是类型的初始化列表

📒5.3使用场景

    定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如 果是自定义类型的对象,需要使用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;
}

六、malloc/free和new/delete的区别

📖共同点:

都是从堆上申请空间,并且需要用户手动释放。

📖不同点:

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

七、内存泄漏

📒7.1什么是内存泄漏

    内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

📒7.2内存泄漏的危害

    长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死

📒7.3内存泄漏分类

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

  • 堆内存泄漏(Heap leak)

    堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一 块内存,用完后必须通过调用相应的free或者delete删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak

  • 系统资源泄漏

    指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

📒7.4检测内存泄漏

    在vs下,可以使用windows操作系统提供的_CrtDumpMemoryLeaks() 函数进行简单检测,该函数只报出了大概泄漏了多少个字节,没有其他更准确的位置信息。

int main()
{
    int* p = new int[10];
    // 将该函数放在main函数之后,每次程序退出的时候就会检测是否存在内存泄漏
    _CrtDumpMemoryLeaks();
    return 0;
}

因此写代码时一定要小心,尤其是动态内存操作时,一定要记着释放。但有些情况下总是防不胜 防,简单的可以采用上述方式快速定位下。如果工程比较大,内存泄漏位置比较多,不太好查时一般都是借助第三方内存泄漏检测工具处理的。

📒7.5避免内存泄漏

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

总结:内存泄漏非常常见,解决方案主要分为以下两种:

  1. 事前预防性型,如智能指针等。
  2. 事后查错型,如泄漏检测工具。

🎁结语: 

     本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位读者三连支持。文章有问题可以在评论区留言,博主一定认真认真修改,以后写出更好的文章。你们的支持就是博主最大的动力。文章来源地址https://www.toymoban.com/news/detail-797433.html

到了这里,关于『C++成长记』内存管理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 告别过去,拥抱未来:一个Java开发者的成长之路

    时光飞逝,不知不觉已经到了大四毕业的时候。回顾这四年的学生生涯,Java开发是让我最为热爱和投入的一部分。在这里,我想和大家分享我在Java开发方面的收获、经验和感悟,同时也向过去的自己告别,迎接未来的挑战。 在大一的时候,我们学习了Java编程基础,当时我并

    2024年02月08日
    浏览(58)
  • 猿创征文|我Java开发那些年陪我成长的工具清单

    📓推荐网站(不断完善中):个人博客 📌个人主页:个人主页 👉相关专栏:CSDN专栏 🏝立志赚钱,干活想躺,瞎分享的摸鱼工程师一枚 本摸鱼程序员也是历经软件专业的四年历程,想在校时期到如经也有几载光阴,不得不说从刚参与到工作中的一脸茫然到现在学会寻找让自

    2024年02月03日
    浏览(60)
  • 【C++内存管理】内存池

    C++17 引入了一个新的内存资源抽象层 - memory_resource 。其主要目的是将内存的分配和回收与具体的数据结构解耦,从而允许开发人员使用不同的内存管理策略,提高内存管理的灵活性。 memory_resource 是一个基类,可以用于创建和配置具有不同内存管理行为的内存池。 1.1 memory_

    2024年02月13日
    浏览(39)
  • C++内存管理(3)——内存池

    利用默认的内存管理操作符 new/delete 和函数 malloc()/free() 在堆上分配和释放内存会有一些额外的开销。 系统在接收到分配一定大小内存的请求时,首先查找内部维护的内存空闲块表,并且需要根据一定的算法(例如分配最先找到的不小于申请大小的内存块给请求者,或者分配

    2024年02月09日
    浏览(39)
  • C++——内存分配与动态内存管理

    🌸作者简介: 花想云 ,在读本科生一枚,致力于 C/C++、Linux 学习。 🌸 本文收录于 C++系列 ,本专栏主要内容为 C++ 初阶、C++ 进阶、STL 详解等,专为大学生打造全套 C++ 学习教程,持续更新! 🌸 相关专栏推荐: C语言初阶系列 、 C语言进阶系列 、 数据结构与算法 本章我们

    2023年04月17日
    浏览(52)
  • 【C++】C++内存管理,模板

    C++中通过 new 和 delete 两个操作符实现动态内存管理 为自定义类型设计,可以在申请空间得同时初始化 使用 new 先申请空间,再调用构造 使用 delete 时先调用析构,再释放空间 operator new 和 operator delete 是系统提供的全局函数,不是运算符重载, 在使用 new 或 delete 时,底层实

    2024年02月01日
    浏览(39)
  • c++ 内存管理一:初识内存分配工具

    前言 侯捷 c++内存管理学习总结笔记。 在C++中,有几种常用的内存分配工具可以帮助进行动态内存管理。 从c++应用程序自上而下,通常会有这样的几种分配内存的方式,当然最终都是直接或间接的调用系统的API。 1 new 和 delete new 和 delete :new操作符用于在堆上分配内存,de

    2024年02月11日
    浏览(43)
  • C语言 — 动态内存管理(动态内存函数)

    本期分为三篇介绍动态内存管理相关内容,关注博主了解更多 博主博客链接:https://blog.csdn.net/m0_74014525 本期介绍动态内存函数,函数如何使用、函数格式、在使用在所需要的注意点及C/C++程序的内存开辟区域 第一篇:C语言 — 动态内存管理(动态内存函数) 第二篇:C语言

    2024年02月14日
    浏览(45)
  • 「探索C语言内存:动态内存管理解析」

    🌠先赞后看,不足指正!🌠 🎈这将对我有很大的帮助!🎈 📝所属专栏:C语言知识 📝阿哇旭的主页:Awas-Home page 目录   引言 1. 静态内存 2. 动态内存 2.1 动态内存开辟函数 2.1.1 malloc函数 2.1.2 calloc函数 2.1.3 realloc函数 2.2 动态内存释放函数 2.2.1 free函数 3. 动态内存的常见

    2024年04月28日
    浏览(37)
  • 【C++初阶】七、内存管理(C/C++内存分布、C++内存管理方式、operator new / delete 函数、定位new表达式)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【C++初阶】六、类和对象(初始化列表、static成员、友元、内部类)-CSDN博客  ==================

    2024年02月05日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包