• 内存 管理


    c/c++中内存分布

    c/c++ 中程序内存的区域划分:

    在这里插入图片描述

    分析下列代码中变量的存储地址:

    int glob = 1;
    static int staglob = 1;
    
    int main()
    {
    	static int a = 1;
    	int b = 2;
    
    	int num1[10] = { 1,2,3,4 };
    	char str1[] = "abcd";
    	const char* pchar = "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);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    存储结构分析
    在这里插入图片描述

    sizeof 与 strlen

    sizeof:计算类型大小

    在这里插入图片描述

    strlen:计算字符长度,遇到 \0 结束

    在这里插入图片描述

    但是考虑一个问题:

    为什么在 malloc 、calloc、realloc 了三个变量空间之后,只进行释放(free)了两次?

    这就是我们接下来要讨论的一个问题。

    c 语言中动态内存管理方式

    malloc

    void malloc( size_t size );
    动态申请空间,但不进行初始化操作
    由于返回值为 void
    类型,因此在使用时需要进行类型强转
    空间使用结束需要手动 free,否则会造成内存泄漏

    malloc 申请到的空间内容为随机值:

    在这里插入图片描述

    calloc

    void calloc( size_t num, size_t size );
    动态申请空间,并将申请到的空间初始化为 0
    由于返回值为 void
    类型,因此在使用时需要进行类型强转
    空间使用结束需要手动 free,否则会造成内存泄漏

    calloc 申请到的空间内容会初始化为 0:

    在这里插入图片描述

    realloc

    void *realloc( void *memblock, size_t size );
    在原有的空间基础上进行空间的重新申请,并会释放回收原有的空间,不会进行初始化操作
    空间使用结束需要手动 free,否则会造成内存泄漏

    realloc 重新开辟新空间并会释放掉旧空间,不会进行初始化:

    在这里插入图片描述

    realloc 重新分配空间之后,会自动释放掉原有空间,因此只需要空间释放一次,并且在重新分配空间时会保持相应位置空间内容不变(扩容:原有大小内容不变,后序新空间为随机值;缩小容量:空间内容与原内容保持一致)

    在这里插入图片描述

    c++ 中动态内存管理

    因为 C++ 兼容 C语言,因此在 C++ 语言当中,依旧可以使用 malloc 、calloc、 realloc 来进行动态内存管理,并且在使用结束之后手动 free 释放空间,避免造成内存泄漏。

    new 与 delete

    C++ 中也常通过 new 和 delete 来进行动态内存的管理:

    new T:申请单个 T 类型的空间
    new T[N]:申请N个T类型的一段连续空间

    new T(a):动态申请一个 T 类型空间,并初始化为 a
    new T[n]{…}:动态申请 n 个 T 类型的连续空间,并在 {} 中进行初始化操作

    delete 变量名 :释放单个空间
    delete[] 变量名:释放连续空间

    在使用 new 与 delete 进行动态内存管理时候要注意对应:
    申请单个空间 new T —> 释放单个空间 delete;
    申请连续空间 new T[] ---->释放连续空间 delete[];

    int main()
    {
    	int* p1 = new int; //申请单个 int 空间
    	int* p2 = new int[10];  //申请 10 个 连续的 int 类型空间
    	
    	//初始化
    	int* p3 = new int(10);
    	int* p4 = new int[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    
    
    	//空间的释放
    
    	delete p1;
    	delete[] p2;     //释放空间应与创建空间匹配----连续的空间
    	delete p3;
    	delete[] p4;      //释放空间应与创建空间匹配----连续的空间
    	 
    	
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    由此可见,C++中动态内存管理 new / delete 不需要进行类型的强转

    自定义类型空间的动态分配

    (1)使用传统的 C 语言 malloc / free 进行动态内存的管理:

    class Test {
    public:
    	Test() :_data(1)
    	{
    		cout << "Test()" << this << endl;
    	}
    	~Test()
    	{
    		cout << "~Test()" << this << endl;
    	}
    private:
    	int _data;
    };
    
    int main()
    {
    	Test* p1 = (Test*)malloc(sizeof(Test));       //单个 Test 类型空间
    	free(p1);
    
    	Test* p2 = (Test*)malloc(sizeof(Test) * 10);   //连续 10 个 Test 类型空间
    	free(p2);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    (2)使用 C++ 中 new / delete 进行动态内存管理:

    class Test {
    public:
    	Test() :_data(1)
    	{
    		cout << "Test()" << this << endl;
    	}
    	~Test()
    	{
    		cout << "~Test()" << this << endl;
    	}
    private:
    	int _data;
    };
    
    int main()
    {
    	Test* p1 = new Test;  //申请单个 Test 空间
    	delete p1;   //释放单个空间
    
    	Test* p2 = new Test[10]; //申请连续10个 Test 空间
    	delete[] p2;   //释放连续空间
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    对于自定义类型的动态内存管理,倘若没有进行匹配释放空间则会造成程序崩溃或内存泄漏:

    在这里插入图片描述

    new 与 delete 的实现

    在申请自定义类型的空间时,new 会调用相应的构造函数对空间进行初始化操作,delete 会调用相应的析构函数进行资源的清理:

    在这里插入图片描述

    operator new 与 operator delete

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

    (一)operator new

    new 申请空间,要么申请成功,要么报错,因此不需要进行判空。

    new 申请新空间(自定义类型):
    (1)内置类型直接调用 operator new 申请空间;
    (2)自定义类型空间,先在堆上进行申请空间,其次调构造函数对空间进行初始化操作。

    在这里插入图片描述
    在这里插入图片描述

    该函数实际上是通过 malloc 来申请空间(operator new 实际上是对 malloc 的封装),当 malloc 申请空间成功直接返回;申请失败,会进行相应不足应对措施:
    检测是否是空间不足------若提供空间不足应对措施,则执行并继续 malloc;若没有提供则会发出 bad_alloc 类型异常。

    在这里插入图片描述

    通过反汇编来查看底层调用:

    在这里插入图片描述
    在这里插入图片描述

    (二)operator delete

    该函数实际上是通过 free 进行释放空间的。

    在这里插入图片描述

    delete 释放空间:
    (1)若是内置类型,直接将空间释放;
    (2)若是自定义类型空间,先调用析构函数进行资源的清理,其次将空间清理掉。

    在这里插入图片描述
    在这里插入图片描述

    通过反汇编来查看底层调用:

    在这里插入图片描述

    内置类型创建连续空间,则会多次调用构造函数对空间进行初始化;
    内置类型释放连续空间,则会多次调用析构函数对空间资源进行清理;

    在这里插入图片描述

    operator new 与 operator delete 支持重载,但一般情况下不用。

    基本概念辨识

    malloc/free 与 new/delete 区别 *****

    相同点:

    两者都是从堆上进行空间的申请,并且需要手动进行释放;

    不同点:

    (1)malloc/free 是函数(需要头文件),new/delete 是操作符(不需要头文件);

    (2)malloc 申请的空间不会进行初始化(内容为随机值),new 可以进行初始化;

    (3)malloc 申请空间时,需要手动计算空间大小并进行传参,new 只需要在其后跟上空间类型即可;

    (4)malloc 返回值为 void* ,因此在使用时候需要进行类型强转,new 不需要;

    (5)malloc 申请空间失败时返回 NULL,因此需要判空,new不需要—直接报错;

    (6)申请自定义类型对象时,malloc/free 指挥开辟空间,不会调用构造函数与析构函数,而 new 在申请空间后会调用自定义类型对象的构造函数进行对象初始化,delete 在释放空间前会调用析构函数完成对象空间中的资源清理工作。

    内存泄漏

    内存泄漏:

    指的是因为疏忽/错误造成程序未能释放已经不能使用的内存的情况。

    内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的操作,因而造成了内存的浪费。

    内存泄漏的危害:

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

    内存泄漏分类:

    (1)堆内存泄漏

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

    (2)系统资源泄漏

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

    欢迎评论留言哈~~~

  • 相关阅读:
    k8s:部署k8s单master节点集群(kubeadm)v1.23.13
    技术速递|宣布为 .NET 升级助手提供第三方 API 和包映射支持
    2022/7/30周总结
    Golang学习记录:基础篇练习(一)
    重启人生计划-大梦方醒
    MySQL 中数据表 id 值 连续 且 自动增长
    表数据结构变动、修复表数据的历史版本兼容解决方案
    解决ros melodic中cv2.imshow报错问题
    开创性的区块链操作系统项目——益智RPG游戏
    【数据结构】经典八大排序算法(万字大总结+动图)
  • 原文地址:https://blog.csdn.net/weixin_46655027/article/details/128117017