C++内存管理基础

简介: 本文将讲述C++内存管理的相关知识。

前言

本文将讲述C++内存管理的相关知识。


🕺作者: 迷茫的启明星

专栏:《C++初阶》


😘欢迎关注:👍点赞🙌收藏✍️留言


🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!


持续更新中~


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);
}


请回答下列问题:


选择题:

选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

globalVar在哪里?____ staticGlobalVar在哪里?____

staticVar在哪里?____ localVar在哪里?____

num1 在哪里?____

char2在哪里?____ *char2在哪里?___

pChar3在哪里?_____ *pChar3在哪里?____

ptr1在哪里?____ *ptr1在哪里?____


填空题:


sizeof(num1) = ;


sizeof(char2) = ; strlen(char2) = ;


sizeof(pChar3) = ; strlen(pChar3) = ;


sizeof(ptr1) = ;


sizeof 和 strlen 区别?


是不是对你来说有那么点挑战,有几个还不确定?


我讲述一下易错的几个点:


选择题


下面字符串的判断容易搞混,char2和*char2是字符数组,是在栈上的。


pCharp是指向常量区的一个指针,指向“abcd”,


解引用的*pChar也就是“abcd”就在常量区


ptr1是指针哦,注意分辨,它申请的空间是在堆上面的


所以*ptr1就是在堆上


填空题


数组num1开辟了十个空间哦,注意看它中括号里面的值,如果一个数组定义时,中括号里面没有值,就只看后面花括号内的值。


字符串大小判断需要记得还有一个‘\0’隐藏起来了,使用sizeof时‘\0’也会算在结果中的,而strlen则不管‘\0’,只算’\0’前面的字符。


还要注意的是,求大小的是“指针”还是“字符串or数组”?


是指针则根据环境的不同,它的大小是4 or 8


以下就是答案了


选择题:

选项: 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


填空题:


sizeof(num1) = 40;


sizeof(char2) = 5 ; strlen(char2) = 4 ;


sizeof(pChar3) = 4 or 8; strlen(pChar3) = 4 ;


sizeof(ptr1) = 4 or 8 ;


sizeof 和 strlen 区别?


sizeof 和 strlen 是两个在 C/C++ 中常用的关键字和函数,它们有着不同的功能和用途。


sizeof 是一个运算符,用于求取数据类型或变量所占用的字节数。它可以用于任何数据类型,包括内置类型、结构体、联合体等。与 sizeof 相关的表达式的结果是一个无符号整数,表示该类型或变量所占用的字节数。例如:


int a;

size_t size = sizeof(int);


上述代码中,第一行定义了一个整型变量 a,第二行使用 sizeof 运算符求取 int 类型所占用的字节数,并将其赋值给一个无符号整数变量 size。


而 strlen 是一个函数,用于求取一个字符串的长度,即其中非空字符的个数。它只能用于字符串类型,即以空字符(\0)结尾的字符数组。当 strlen 函数遇到空字符时,它就会停止统计字符数量,返回字符串的长度。例如:


char str[] = "hello world";

size_t len = strlen(str);


上述代码中,第一行初始化了一个字符数组,其中包含了一个以空字符结尾的字符串;第二行使用 strlen 函数求取了这个字符串的长度,并将其赋值给一个无符号整数变量 len。


图示:

image.png



说明:


栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。

内存映射段是高效的I/O映射方式,用于装载一个动态内存库。用户可使用系统接口创建共享内存,做进程间通信。(之后会在linux中讲的)

堆用于程序运行时动态内存分配,堆是可以向上增长的。

数据段–存储全局数据和静态数据。

代码段–可执行的代码/只读常量

小结:这一个知识点,我们配合题目,解析,图示很容易理解的,记住就好。


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

在学习 C++内存管理方式前,我们先回顾一下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 );
}


我相信只要你学过c语言都不会对它们陌生的,我就来帮你总结一下它们。


1.malloc/calloc/realloc的区别是什么?


malloc、calloc 和 realloc 都是动态分配内存的函数。


malloc 函数用于申请指定大小的内存块,返回该内存块首地址的指针。它只负责分配内存,并不会对分配的内存进行初始化,所以分配的内存中可能包含垃圾数据。

calloc 函数在申请内存的同时会对内存进行清零,可以防止出现未初始化的垃圾值。它的参数为所需要的元素个数和每个元素占用字节的大小,返回值是该内存块首地址的指针。

realloc 函数用于重新分配已有内存的大小。它接受两个参数:**第一个参数是原先分配的内存块首地址,第二个参数是新分配的内存大小。**如果新分配的大小小于旧大小,则在新内存末尾处丢弃一部分内存;如果新分配的大小大于旧大小,则在新内存末尾处添加一些内存。如果分配成功,则返回该内存块首地址的指针,否则返回 NULL。

这里需要free(p2)吗?


在 Test 函数中,p2 是通过 calloc 函数申请并初始化了一个大小为 4 个 int 类型元素的内存块。然后使用 realloc 函数将 p2 指向的内存块扩展至 10 个 int 类型元素大小,并返回了新的内存块地址,同时释放了原先 p2 指向的内存块。这意味着我们不需要再次释放 p2 所指向的内存块,因为它已经被 realloc 函数转移并释放了。最后,我们释放了 p3 所指向的内存块,以避免内存泄漏。


需要注意的是,使用动态内存分配函数时,一定要注意内存的申请和释放的对应关系,否则就容易出现内存泄漏或野指针的问题。


拓展:


malloc的实现原理?


malloc 是 C 标准库中的一个动态内存分配函数,它的原理是从堆(Heap)区域中申请一块指定大小的内存,并返回一个指向该内存块首地址的指针。下面是 malloc 这个函数大致的实现原理:


首先,malloc 函数会根据用户请求的内存大小,计算出需要申请的总内存量。在这个过程中,可能会添加一些额外的字节来记录内存块的大小和其他信息。这样做的目的是为了在释放内存时能够知道需要释放多少内存和维护当前内存块的状态。


接下来,malloc 函数会调用底层的系统函数(如 sbrk 或 mmap),向操作系统申请一块连续的虚拟内存空间,这块空间将作为内存池,用于存储用户请求的内存块。


为了记录内存块的大小、是否被占用等信息,malloc 函数会将每一个内存块都分成两部分:头部(Header)和数据区域。头部通常包含以下信息:


前后内存块的大小

当前内存块的大小

内存块是否占用

当有新的内存请求时,malloc 函数会按照一定的算法在内存池中寻找一块足够大的未被占用的内存块,并返回该内存块首地址的指针。


如果没有足够大的内存块可供使用,malloc 函数将会调用系统函数扩展内存池的大小,以便存储更多的内存块。对于类 Unix 系统,sbrk 函数可以从操作系统请求内存池的空闲空间;而对于现代操作系统,mmap 函数则常常用于扩展内存池。


总结:malloc 函数的主要原理是从操作系统中申请一块虚拟内存的空间,并将其作为内存池,接着使用成本较低的数据结构(如链表)管理内存池中的内存块,以满足用户动态内存分配的需求。


3. C++中动态内存管理

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


3.1 new/delete操作内置类型

看下面代码:


注释中就是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;
}


图示:


image.png


它和前文讲C语言的malloc/free有区别吗?


针对内置类型,new/delete跟malloc/free没有本质的区别,只有用法的区别

new/delete 用法简化了

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。


3.2 new和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/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数
    A* p1 = (A*)malloc(sizeof(A));//C
    //1、堆上申请空间  2、调用构造函数初始化
    A* p2 = new A(1);
    free(p1);//C
    // 1、调用析构函数清理对象中资源 2、释放空间
    delete p2;
    // 内置类型是几乎是一样的
    int* p3 = (int*)malloc(sizeof(int)); // C
    int* p4 = new int;
    free(p3);//C
    delete p4;
    A* p5 = (A*)malloc(sizeof(A)*10);//C
    A* p6 = new A[10];
    free(p5);
    delete[] p6;
    //在C++11中还有一种初始化的方式,先了解即可,之后会再讲的
    A* p7 = new A[2]{1,2};
  A* p8 = new A[2]{ A(1), A(2) };
    delete[] p7;
    delete[] p8;
    return 0;
}
//结论:new/delete 是为自定义类型准备的。
// 不仅在堆中申请出来,还会调用构造和析构初始化和清理



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


4. operator new与operator delete函数

4.1 operator new与operator 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)
{
    // 尝试分配size大小字节
    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);
}

这是 C++ STL 库中定义的单个对象分配操作符 new 的默认实现。在 C++ 中,当我们需要分配一块内存来存储一个对象时,可以使用这个 new 运算符来完成分配内存的工作。


具体来说,这段代码的主要功能是从堆上申请一块指定大小的内存,并在内存不足时抛出 bad_alloc 异常(该异常类型是 C++ 标准库内置的一种异常类型,表示内存申请失败)。其具体流程如下:


首先,该函数会调用 malloc 函数,尝试在堆上申请一块大小为 size 字节的内存块。


如果 malloc 函数返回的指针为 nullptr,说明堆内存不足,此时进入 while 循环。


在循环内部,该函数会调用 _callnewh 函数,尝试调用用户自定义的 new_handler 函数处理内存不足的情况。new_handler 是一个函数指针类型,它指向一个用户自定义的函数,当堆内存不足时,C++ 运行时系统就会自动调用该函数进行内存管理。如果 new_handler 函数可以成功分配内存,则函数将退出循环并返回新分配的内存地址;否则,程序将继续等待内存的释放或调用其他 new_handler。


如果 new_handler 函数返回失败(即返回值为 nullptr),说明堆内存不足,此时函数会抛出 bad_alloc 异常,通知调用方内存分配失败。


如果 malloc 函数成功地分配了指定大小的内存块,则该函数将返回新分配的内存块首地址的指针。


operator delete: 该函数最终是通过free来释放空间的

void operator delete(void *pUserData)
{
    _CrtMemBlockHeader * pHead;
    RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
    if (pUserData == NULL)
        return;
    _mlock(_HEAP_LOCK);  /* 释放其他线程 */
    __TRY
        /* 获取指向内存块首地址的指针 */
        pHead = pHdr(pUserData);
    /* 验证块类型 */
    _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
    _free_dbg( pUserData, pHead->nBlockUse );
    __FINALLY
        _munlock(_HEAP_LOCK);  /* 释放其他线程 */
    __END_TRY_FINALLY
    return;
}



这是 C++ 标准库中定义的单个对象释放操作符 delete 的默认实现。在 C++ 中,当我们完成使用一个动态分配的对象后,需要使用 delete 运算符来释放分配的内存空间。


该函数的主要功能是根据参数 pUserData 指针释放内存,并进行必要的内存结构清理和管理。其具体流程如下:


首先,该函数调用 _CrtMemBlockHeader 结构体指针 pHead,获取指向内存块头部的指针。


接下来,该函数会调用 _free_dbg 函数,释放指针 pUserData 所指向的内存块。_free_dbg 函数与标准的 free 函数类似,都是用于释放动态内存的函数,但它还会检查内存泄漏、越界访问等情况,并定位出错位置,对调试程序有很大的帮助。


在释放内存块的同时,该函数还会验证该内存块的类型是否合法,以防止误操作或非法指针释放的出现。


最后,该函数使用 _mlock 和 _munlock 函数,对堆进行线程同步管理,保证多线程环境下的内存空间分配和释放的正确性和线程安全性。


free 的实现


#define  free(p)        _free_dbg(p, _NORMAL_BLOCK)


这是 C++ 标准库中的一个宏定义,用于释放动态分配的内存。在 C++ 中,我们可以使用 free 函数来释放通过 malloc 或 calloc 分配的内存在堆上的内存空间。


该宏定义的主要作用是将 free 函数的调用转换为 _free_dbg 函数的调用,并将第二个参数设置为 _NORMAL_BLOCK,以指定使用正常内存块的方式进行内存释放操作。其中 _free_dbg 函数已经在前一个问题中有所提及,它可以定位内存泄漏、越界访问等问题,并对程序进行调试和错误排查。


这样做的好处是,在程序调试时能够更有效地检测到内存错误和不当操作,帮助程序员更快地定位内存问题,提高程序的健壮性和可靠性。同时,由于 free 宏本质上是对 _free_dbg 函数的封装,因此没有额外的性能开销,可以保证程序运行效率的同时,提高程序的调试和维护的便利性。


5. new和delete的实现原理

5.1 内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:

new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。


5.2 自定义类型

new的原理

调用operator new函数申请空间

在申请的空间上执行构造函数,完成对象的构造

delete的原理

在空间上执行析构函数,完成对象中资源的清理工作

调用operator delete函数释放对象的空间

new T[N]的原理

调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请

在申请的空间上执行N次构造函数

delete[]的原理

在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理

调用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;
}



解释:


这段代码主要演示了如何通过定位 new 和定位 delete 来分别实现动态分配内存和释放内存的过程。


在 C++ 中,我们可以使用 new 运算符来动态地分配内存,并在分配内存的同时直接构造对象。而使用 delete 运算符可以释放动态分配的内存,并在释放内存的同时直接调用析构函数进行对象的上下文清理。通常情况下,在使用 new 时会自动调用构造函数,使用 delete 时会自动调用析构函数。


但是在一些特殊场景下,我们可能需要手动控制对象的构造和析构过程,以保证程序的正确性和稳定性。这时,就需要使用定位 new 和定位 delete 来完成内存空间的申请和释放过程。


在这段代码中,首先使用 malloc 函数分配了一个大小为 sizeof(A) 的内存块,并将其转换成了类型为 A* 的指针 p1。注意,此时 p1 指向的还只是分配的内存空间,并没有调用构造函数初始化对象。


接着,使用定位 new 运算符 new(p1)A ,在 p1 指向的空间上直接创建了一个 A 类对象。此时,由于没有传入参数,默认调用了构造函数 A::A(int a = 0) ,输出了一条信息表示对象创建成功。


接下来,使用定位 delete 运算符 p1->~A() ,在 p1 指向的空间上直接调用了 A 类对象的析构函数。注意,此时并没有立即释放内存空间,只是对对象进行了上下文清理工作。


最后,通过调用 free(p1) 函数,释放了动态分配的内存空间,完成了整个过程。


接下来,又使用了 operator new 运算符动态分配内存,但与 malloc 函数不同,operator new 更加灵活,并且可以自动管理内存池,提高了程序性能。在这里,它同样被用来分配一个大小为 sizeof(A) 的内存块,并将其转换成类型为 A* 的指针 p2。


接着,同样使用定位 new 运算符 new(p2)A(10) ,在 p2 指向的空间上直接创建了一个 A 类对象,并传入了参数 10。此时,构造函数 A::A(int a = 0) 将会带着参数 10 进行调用,输出一条信息表示对象创建成功。


接下来,使用定位 delete 运算符 p2->~A() ,在 p2 指向的空间上直接调用了 A 类对象的析构函数。


最后,通过调用 operator delete(p2) 函数,释放了动态分配的内存空间,并自动回收内存池,完成了整个过程。


7. 常见面试题

7.1 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在释放空间前会调用析构函数完成空间中资源的清理

7.2 内存泄露

7.2.1 什么是内存泄漏,内存泄漏的危害

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

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

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;
}

解释:


首先,在第 4 行和第 5 行,分别使用了 malloc 函数和 new 运算符动态地分配了一个 int 类型的内存块,并将其转换成类型为 int* 的指针 p1 和 p2。但是,在程序执行结束时,没有调用对应的释放函数或运算符,导致了内存泄漏的问题。这样的内存泄漏不仅会占用系统资源,还会导致程序性能下降,严重情况下可能会导致系统崩溃等问题。因此,需要在程序执行结束时,显式调用 free 函数和 delete 运算符,释放动态分配的内存空间,避免内存泄漏的问题。


接着,在第 8 行到第 10 行,使用了 new 运算符动态地分配了一个长度为 10 的 int 类型数组,并将其转换成类型为 int* 的指针 p3。但是,在第 11 行调用了一个函数 Func(),该函数抛出了异常,导致后续的代码未被执行,包括第 12 行的释放 p3 数组的操作。这样就会导致程序存在异常安全问题,即当程序抛出异常时,可能会造成内存泄漏或其他非预期的后果。为了解决这个问题,可以使用 RAII(资源获取即初始化)技术,通过定义局部对象来管理资源,在函数结束时自动调用析构函数,从而确保资源的正确释放。


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.


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


在linux下内存泄漏检测:linux下几款内存泄漏检测工具

在windows下使用第三方工具:VLD工具说明

其他工具:内存泄漏工具比较


7.2.4如何避免内存泄漏

工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。ps:这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要之后讲的智能指针来管理才有保证。

采用RAII思想或者智能指针来管理资源。

有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。出问题了使用内存泄漏工具检测。ps:不过很多工具都不够靠谱,或者收费昂贵。

总结一下:

内存泄漏非常常见,解决方案分为两种:


1、事前预防型。如智能指针等。


2、事后查错型。如泄漏检测工具。


后记

我已经快一个没有更新了,前段时间是期中考试四门专业课有点忙,今后继续保持记录和输出,我还会开一个刷题专栏,感谢大家支持!!!


相关文章
|
28天前
|
存储 缓存 编译器
【硬核】C++11并发:内存模型和原子类型
本文从C++11并发编程中的关键概念——内存模型与原子类型入手,结合详尽的代码示例,抽丝剥茧地介绍了如何实现无锁化并发的性能优化。
|
9天前
|
存储 程序员 编译器
什么是内存泄漏?C++中如何检测和解决?
大家好,我是V哥。内存泄露是编程中的常见问题,可能导致程序崩溃。特别是在金三银四跳槽季,面试官常问此问题。本文将探讨内存泄露的定义、危害、检测方法及解决策略,帮助你掌握这一关键知识点。通过学习如何正确管理内存、使用智能指针和RAII原则,避免内存泄露,提升代码健壮性。同时,了解常见的内存泄露场景,如忘记释放内存、异常处理不当等,确保在面试中不被秒杀。最后,预祝大家新的一年工作顺利,涨薪多多!关注威哥爱编程,一起成为更好的程序员。
|
2月前
|
存储 缓存 C语言
【c++】动态内存管理
本文介绍了C++中动态内存管理的新方式——`new`和`delete`操作符,详细探讨了它们的使用方法及与C语言中`malloc`/`free`的区别。文章首先回顾了C语言中的动态内存管理,接着通过代码实例展示了`new`和`delete`的基本用法,包括对内置类型和自定义类型的动态内存分配与释放。此外,文章还深入解析了`operator new`和`operator delete`的底层实现,以及定位new表达式的应用,最后总结了`malloc`/`free`与`new`/`delete`的主要差异。
68 3
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
207 4
|
3月前
|
存储 程序员 编译器
简述 C、C++程序编译的内存分配情况
在C和C++程序编译过程中,内存被划分为几个区域进行分配:代码区存储常量和执行指令;全局/静态变量区存放全局变量及静态变量;栈区管理函数参数、局部变量等;堆区则用于动态分配内存,由程序员控制释放,共同支撑着程序运行时的数据存储与处理需求。
214 22
|
3月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
3月前
|
存储 C语言 C++
【C++打怪之路Lv6】-- 内存管理
【C++打怪之路Lv6】-- 内存管理
63 0
【C++打怪之路Lv6】-- 内存管理
|
3月前
|
存储 C语言 C++
【C/C++内存管理】——我与C++的不解之缘(六)
【C/C++内存管理】——我与C++的不解之缘(六)
|
3月前
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
105 1
|
3月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
622 1