C/C++内存管理详解

目录

一、C++内存分布

二、C语言与C++内存管理方式

1、C语言中动态内存管理方式:malloc/calloc/realloc/free

2、C++中的内存管理方式:new/delete

[三、operator new与operator delete函数](#三、operator new与operator delete函数)

1、函数概念:

2、函数使用:

3、底层原理:

四、new和delete的实现原理

1、对于内置类型:

2、对于自定义类型:​​​​​​​

五、内存泄漏

1、概念:

2、内存泄漏分类:

3、避免内存泄漏:


一、C++内存分布

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

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

**2、内存映射段:**是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存,做进程间通信。

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

4、数据段(静态区) --存储全局数据和静态数据。

5、代码段(常量区): 可执行的代码/只读常量。

二、C语言与C++内存管理方式

1、C语言中动态内存管理方式:malloc/calloc/realloc/free

**malloc:**在内存的动态存储区中分配一块长度为size字节的连续区域,参数size为需要内存空间的长度,返回该区域的首地址

**calloc:**与malloc类似,不同点是函数calloc() 会将所分配的内存空间中的每一位都初始化为零

**realloc:**给一个已经分配了地址的指针重新分配空间,可以做到对动态开辟内存大小的调整。

cpp 复制代码
void Test1()
{
	char* ptr1 = (char*)malloc(sizeof(char));
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 10);

	free(ptr1);
	free(ptr3);
}

2、C++中的内存管理方式:new/delete

C++的内存管理方式:通过newdelete操作符进行动态内存管理

为什么c++要出一套自己的内存管理规则?

1、C语言的申请内存比较繁琐,要强转,要计算大小,要检查返回值

2、无法对自定义类型的空间申请做出很好的控制(针对自定义类型能更好的初始化与清理)

(1)new/delete操作内置类型:

cpp 复制代码
void Test2()
{
	// 动态申请一个int类型的空间
	int* ptr1 = new int;
	// 动态申请一个int类型的空间并初始化为10
	int* ptr2 = new int(10);
	// 动态申请10个int类型的空间
	int* ptr3 = new int[3];
	// 动态申请10个int类型的空间并初始化/不完全初始化
	int* ptr4 = new int[10] {1, 2, 3, 4, 5};

	delete ptr1;
	delete ptr2;
	delete[] ptr3;
	delete[] ptr4;
}

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用 new[]和delete[],注意:匹配起来使用。
(2)、new和delete操作自定义类型

new和delete相比于malloc和free的优点:

申请空间时:malloc只开空间,new不仅开空间还会调用构造函数初始化。

释放空间时:delete会调用析构函数,free不会。

当我们运行以下程序可知:

cpp 复制代码
class A
{
public:
	A(int a = 0)
	{
		cout << "A()  // 调用构造函数" << endl;
	}
	~A()
	{
		cout << "~A()  // 调用析构函数" << endl;
	}

private:
	int _a;
};

int main()
{
	A* a = new A(1);
	delete a;
	return 0;
}

且new在申请内存时不需要像malloc一样检查合法性,new申请空间失败会自动抛异常

cpp 复制代码
void Test3()
{
	//malloc失败,返回空指针
	int* ptr1 = (int*)malloc(sizeof(int) * 10);
	assert(ptr1); //malloc出来的需要检查合法性
	
	int* ptr2 = new int; 
	//new出来的不需要检查合法性,失败会自动抛异常
}

三、operator new与operator delete函数

1、函数概念:

注意:operator new和operator delete不是对new和delete的重载,是库函数。

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

operator new的本质是对malloc的封装。

  • 该函数实际上就是通过malloc来申请空间,申请成功时直接返回,失败时抛异常

operator delete的本质是对free的封装。

2、函数使用:

cpp 复制代码
void Test4()
{
	int* ptr2 = (int*)malloc(sizeof(int));
	free(ptr2);

	// 使用方法与malloc/free相似
	int* ptr1 = (int*)operator new(sizeof(int));
	operator delete(ptr1);
}

operator new/operator delete与malloc/free的相同点:

  • 功能一样,不会去调用构造函数和析构函数。

不同点:

  • operator new不需要检查开辟空间的合法性,失败就抛异常。

3、底层原理:

new的底层原理:转换成调用operator new + 构造函数

delete的底层原理:转换成调用operator delete + 析构函数

operator new与operator delete的类专属重载:

为了避免有些情况下我们反复的向堆申请释放空间,于是产生池化技术(内存池),直接找内存池申请释放空间,此时效率更高更快。new/delete的类专属重载就是在new调用operator new的时候就可以走内存池的机制从而提高效率。

内存池:

**内存池是一种内存管理策略,它通过预先分配一定数量的、大小固定的内存块来优化内存分配性能和减少内存碎片化,从而提高资源利用率。**内存池允许应用程序快速、高效地获取和释放内存,而不需要频繁地从系统内存中分配和释放小块内存。

四、new和delete的实现原理

1、对于内置类型:

对于内置类型,new和malloc,delete和free基本类似,

不同点为:

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

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

五、内存泄漏

1、概念:

内存泄漏是一种编程错误,发生在一个程序重复地分配内存但未能释放已不再使用的内存。这将导致系统内存逐渐耗尽,最终可能使得程序或整个系统变得不稳定,甚至崩溃。

内存泄漏的危害:

内存泄漏会导致程序持续占用内存而不释放,造成系统性能下降。

当内存泄漏累积到一定程度时,程序运行速度变慢,响应时间变长。

当大量内存被泄漏时,会导致系统内存不足,从而造成使系统崩溃或死锁等问题。

2、内存泄漏分类:

在C/C++程序中一般我们主要关心以下内存泄漏:

堆内存泄漏(Heap leak):

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

系统资源泄漏:

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

3、避免内存泄漏:

1、养成良好的编码规范,确保每次分配内存后都有对应的释放内存的操作。

2、使用垃圾回收机制(一种自动管理内存的方式,可以自动回收无用的内存,防止内存泄漏)。

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

4、进行内存泄露测试:可以使用一些内存泄露测试工具,比如Valgrind、Memory Profiler等。

相关推荐
芊寻(嵌入式)几秒前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
獨枭2 分钟前
C++ 项目中使用 .dll 和 .def 文件的操作指南
c++
霁月风5 分钟前
设计模式——观察者模式
c++·观察者模式·设计模式
橘色的喵6 分钟前
C++编程:避免因编译优化引发的多线程死锁问题
c++·多线程·memory·死锁·内存屏障·内存栅栏·memory barrier
一颗松鼠9 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
有梦想的咸鱼_11 分钟前
go实现并发安全hashtable 拉链法
开发语言·golang·哈希算法
海阔天空_201316 分钟前
Python pyautogui库:自动化操作的强大工具
运维·开发语言·python·青少年编程·自动化
天下皆白_唯我独黑23 分钟前
php 使用qrcode制作二维码图片
开发语言·php
夜雨翦春韭27 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
小远yyds29 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js