Cpp内存管理(7)

news2024/11/17 6:37:36

文章目录

  • 前言
  • 一、C/C++内存区域划分
  • 二、C/C++动态内存管理
    • C语言动态内存管理
    • C++动态内存管理
      • 对于内置类型
      • 对于自定义类型
  • 三、new和delete的底层实现
  • 四、new和delete的实现原理
  • 五、定位new
  • 六、malloc/free和new/delete的区别
  • 总结


前言

  软件开发过程中,内存管理的重要性不言而喻
  因此我们有必要了解一下C++中关于内存管理的一些特性

C++对内存的自由度使其获得了更高的性能,以及更高的难度。
内存泄漏往往是每个C++学习者绕不开的错误, 而内存管理的水平高低也能看出一个编程者的能力


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

你能说出globalVal、staticGlobalVar、staticVar、localVar、num1、*num1存在哪里吗?

在C++中,程序的内存区域从低地址到高地址划分如下:

  1. 代码段:存储可执行程序的代码和只读常量
  2. 数据段:存储已初始化的全局变量和静态变量
  3. 堆:用于程序运行时动态内存分配,从低地址向高地址增长
  4. 栈:又叫堆栈,存储非静态局部变量/函数参数和返回值等,从高地址向低地址增长

图形语言如下:
在这里插入图片描述

  我们倒回去看,我挑几个比较有意思的来细讲,你也可以重点关注一下,staticVar、char2、*char2、pChar3、*pChar3、*ptr2分别存储在哪里

变量名存储段
staticVar静态局部变量,存在数据段
char2字符指针变量,存在栈
*char2数组元素,存在栈,不是存在代码段,因为不是只读!!!
pChar3局部指针变量,存在栈
*pChar3只读变量,存在代码段
*ptr2动态分配的内存,存在堆

哦,对了,关于上面所说的“栈是向下增长的,而堆是向上增长的”,
简单来说就是在栈区开辟空间,先开辟的空间地址较高,而在堆区开辟空间,先开辟的空间地址较低
你可以通过以下代码来验证一下:

// 实在验证不出来就算了,这个跟编译器等环境关系很大
#include <iostream>
using namespace std;
int main()
{
	// 栈区开辟空间,先开辟的空间地址高
	int a = 10;
	int b = 20;
	cout << &a << endl;
	cout << &b << endl;

	// 堆区开辟空间,先开辟的空间地址低
	// 具体实现中,关于堆我们可能会验证失败
	// 你可以试着想一下这是为什么?
	int* c = (int*)malloc(sizeof(int)* 10);
	cout << c << endl;
	
	// free(c);加了这行,发现两个输出相同
	// 说明在堆区,后开辟的空间也有可能位于前面某一被释放的空间位置
	
	int* d = (int*)malloc(sizeof(int)* 10);
	cout << d << endl;
	
	return 0;
}

二、C/C++动态内存管理

C语言动态内存管理

  我们来回顾一下几种用于动态分配内存的函数:malloc、calloc、realloc 和 free,这些函数用于在程序运行时动态地分配和释放内存

malloc:用于分配指定大小的内存块,内存中的内容未初始化
calloc:类似于 malloc,但会将内存初始化为零。它的参数为元素的数量和每个元素的大小
realloc:用于调整之前分配的内存块的大小,如果新大小大于原大小,可能会移动内存块的位置

来个示例代码:

int* ptr1 = (int*)malloc(sizeof(int) * 4);  // 分配4个int类型大小的内存块
int* ptr2 = (int*)calloc(4, sizeof(int));   // 分配并初始化4个int类型大小的内存块
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4); // 重新分配内存

free(ptr1);
free(pter2);
free(ptr3);

C++动态内存管理

  C++继承了C语言的内存管理方式,并在此基础上引入了newdelete操作符,提供更方便的动态内存管理机制,这并不奇怪,因为C++本来就是祖师爷觉得C麻烦,在其基础上发展而来的

new 和 delete 适用于对象的动态内存分配,并且会自动调用构造函数和析构函数,这很重要

对于内置类型

  对于内置类型,其实 new 和 delete 在底层上多大的差别,只是使用的规则要有所区分

以下相对应内容等价

	// 动态申请单个int类型的空间
	int* p1 = new int; //申请
	delete p1; //销毁
	
	// 动态申请单个int类型的空间
	int* p2 = (int*)malloc(sizeof(int)); //申请
	free(p2); //销毁

	// 动态申请10个int类型的空间
	int* p3 = new int[10]; //申请
	delete[] p3; //销毁

	// 动态申请10个int类型的空间
	int* p4 = (int*)malloc(sizeof(int)* 10); //申请
	free(p4); //销毁

	// 动态申请单个int类型的空间并初始化为10
	int* p5 = new int(10); //申请 + 赋值
	delete p5; //销毁
	
	// 动态申请一个int类型的空间并初始化为10
	int* p6 = (int*)malloc(sizeof(int)); //申请
	*p6 = 10; //赋值
	free(p6); //销毁

	// 动态申请10个int类型的空间并初始化为0到9
	int* p7 = new int[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //申请 + 赋值
	delete[] p7; //销毁

	// 动态申请10个int类型的空间并初始化为0到9
	int* p8 = (int*)malloc(sizeof(int)* 10); //申请
	for (int i = 0; i < 10; i++) //赋值
	{
		p8[i] = i;
	}
	free(p8); //销毁

申请和释放单个元素的空间,使用new和delete操作符;申请和释放连续的空间,使用new[ ]和delete[ ]

对于自定义类型

 new会调用构造函数,delete会调用析构函数,而malloc和free不会,原理下文再来解释

三、new和delete的底层实现

 new和delete并不是函数,而是用户进行动态内存申请和释放的操作符

但是其底层还是需要调用函数
且虽然函数名中带operator,但并不是重载函数,具有很强的误导性!!!

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

我们来看一下operator new 和 operator free 的底层实现:

void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
	// try to allocate size bytes
	void* p;
	while ((p = malloc(size)) == 0) // 注意这里,就是malloc
		if (_callnewh(size) == 0)
		{
			// report no memory
			// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
			static const std::bad_alloc nomem;
			_RAISE(nomem);
		}
 
	return (p);
}
 
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); // 注意这里,就是free
 
	__FINALLY
		_munlock(_HEAP_LOCK);  /* release other threads */
	__END_TRY_FINALLY
 
	return;
}

看不懂?看不懂就对了,我也看不懂,但是你注意一下我两个特意的注释点

 可以看出 operator new 实际上也是通过 malloc 来申请空间的,如果 malloc 申请空间成功就直接返回,如果失败则执行用户提供的应对措施,如果用户提供该措施则继续申请空间,否则抛出异常

其实,这也叫封装,就像引用的底层也是用指针的方式实现的

在这里插入图片描述

四、new和delete的实现原理

内置类型无非就是包一下,加个抛出异常,而对于自定义类,就复杂了

一、new的原理
  调用operator new函数申请空间,在申请的空间上执行构造函数,完成对象的构造

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

三、new T[N]的原理
  调用operator new[]函数,而operator new[]函数实际上又会调用operator new函数完成N个T类型对象的空间申请,在申请的空间上执行N次构造函数

四、delete[]的原理
  在空间上执行N次析构函数,完成N个对象的资源清理调用operator delete[]函数,而operator delete[]函数又会调用operator delete函数来释放空间

五、定位new

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

使用格式
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表

  其实这个内容,你可以暂时做个了解,因为这个一般搭配内存池使用,这又牵扯到一个概念叫做池化技术,而内存池分配出的内存没有初始化,所以如果是自定义类型的对象,就需要使用定位new表达式进行显示调用构造函数进行初始化

#include <iostream>
using namespace std;

class A
{
public:
	A(int a = 0) // 构造函数 
		:_a(a)
	{}

	~A() // 析构函数
	{}
private:
	int _a;
};

int main()
{
	// new(place_address)type 形式
	A* p1 = (A*)malloc(sizeof(A));
	new(p1)A;

	// new(place_address)type(initializer-list) 形式
	A* p2 = (A*)malloc(sizeof(A));
	new(p2)A(2021);

	// 析构函数也可以显示调用
	// 这就是为什么只有定位new,没有定位delete的缘故
	p1->~A();
	p2->~A();
	
	return 0;
}

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

首先,它们的共同点都是都是从 堆 上申请空间,并且需要用户手动释放

不同点,那就多了:

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

总结

  哈,本节内容还是蛮轻松惬意的,至少跟类和对象比起来是这样
  那么现在,我们来接着往下看模板
  相信我,这会更有意思!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2167324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3中echarts柱状图横轴文字太多放不下怎么解决

问题&#xff1a;在做数据展示的时候&#xff0c;使用的是echarts&#xff0c;遇到了个问题&#xff0c;就是数据过多&#xff0c;但是设置的x轴的文字名称又太长&#xff0c;往往左边第一个或右边最后一个的名称展示不全&#xff0c;只有半个。 从网上找到了几种办法&#xff…

进击J8:Inception v1算法实战与解析

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、实验目的&#xff1a; 了解并学习图2中的卷积层运算量的计算过程了解并学习卷积层的并行结构与1x1卷积核部分内容&#xff08;重点&#xff09;尝试根据模…

pdf转换成word有哪些方法?10种将PDF转成word的方法

pdf转换成word有哪些方法&#xff1f;在数字化世界中&#xff0c;PDF和word文档是最常用的两种文件格式。PDF凭借其固定布局和跨平台的兼容性&#xff0c;成为了文件分享的首选&#xff0c;而word则因其灵活的编辑功能被广泛应用于各种文本处理需求。在许多情况下&#xff0c;我…

高效IaC测试利器:AlibabaCloud ROS-Tool-Iact3快速上手

在云计算时代&#xff0c;基础设施即代码&#xff08;Infrastructure as Code, IaC&#xff09;已成为提升运维效率、实现自动化部署的重要手段。为了进一步简化IaC模板的测试流程&#xff0c;alibabacloud-ros-tool-iact3工具应运而生&#xff0c;它专为Terraform和阿里云资源…

再次重温 Spring 中 Bean 的生命周期

Bean的生命周期 Spring中的bean的生命周期主要包含四个阶段&#xff1a;实例化Bean --&#xff1e; Bean属性填充 --&#xff1e; 初始化Bean --&#xff1e;销毁Bean 首先是实例化Bean&#xff0c;当客户向容器请求一个尚未初始化的bean时&#xff0c;或初始化bean的时候需要…

Java 导出excel

1.导出excel 带合计 如&#xff1a; public void export(DriverAndGuestMealQueryVO vo) {DriverAndGuestMealListDTO riceLiquidationPage page(vo);// 创建一个Excel工作簿Workbook workbook new XSSFWorkbook();// 创建一个工作表sheetSheet sheet workbook.createShee…

网络通信——DHCP

目录 一.DHCP应用场景 二.通信过程 三.DHCP报文 四.DHCP通信原理 &#xff08;1&#xff09;租借过程 &#xff08;2&#xff09;DHCP 租期更新 &#xff08;3&#xff09;DHCP重绑定 五.一般路由器的DHCP支持两种地址池 &#xff08;1&#xff09;接口地址池 &…

Html jquery下拉select美化插件——selectFilter.js

1. Html jquery下拉select美化插件——selectFilter.js jQuery是一个广泛使用的JavaScript库&#xff0c;它简化了DOM操作、事件处理、动画以及Ajax交互&#xff0c;使得开发者能更高效地构建交互式网页。在本案例中&#xff0c;jquery.selectlist.js插件正是基于jQuery构建的&…

复旦大学附属中山医院院士团队论文遭遇质疑

近日&#xff0c;一篇发表于肝脏领域顶级期刊《Hepatology》(IF:17.1;Q1&#xff09;杂志的肝细胞癌研究论文因图像数据的相似性问题受到质疑。该论文题为‘Protein tyrosine phosphatase receptor S acts as a metastatic suppressor in hepatocellular carcinoma by control …

找不到concrt140.dll怎么修复,这4种方法可轻松搞定

1. concrt140.dll 定义 1.1 系统文件 concrt140.dll 是一个系统文件&#xff0c;属于 Windows 操作系统中重要的动态链接库&#xff08;DLL&#xff09;之一。它通常位于系统的 System32 或 SysWOW64 文件夹中&#xff0c;是 Microsoft Visual C 2015 Redistributable 包的一…

如何在产品上扩大储存?教你一招简单好用的!

你是不是经常遇到需要扩大库存的问题&#xff1f;毕竟总是有很多文件需要存储&#xff1a;视频、音频、文件。。。 但是芯片的空间寸土寸金呀&#xff01; 内部不够只能外扩&#xff0c;然后就是要编写各种驱动&#xff0c;还有Flash替换。。。怎么听着就头疼&#xff01; 教…

【一文带你找到答案!】你了解文档透明加密系统吗?文档透明加密系统有什么功能?

在当今数字化时代&#xff0c;信息安全已成为企业和个人不可忽视的重要议题。 文档透明加密系统作为保护敏感信息的关键技术之一&#xff0c;正逐渐受到广泛关注和应用。 本文将带您深入了解文档透明加密系统&#xff0c;并详细解析其各项功能。 一、文档透明加密系统概述 文…

linux常见指令与权限【第四课】

19.tar指令&#xff08;重要&#xff09;&#xff1a;打包/解包&#xff0c;不打开它&#xff0c;直接看内容 tar [-cxtzjvf] &#xff1a; -c &#xff1a;建立一个压缩文件的参数指令 (create 的意思 ) &#xff1b; -x &#xff1a;解开一个压缩文件的参数指令&#xff…

Java线程池和原子性

文章目录 前言1 线程池1.1 线程池概述1.1.1 线程池存在的意义1.1.2 Executors默认线程池 1.2 线程状态介绍1.2.1 线程状态源码1.2.2 线程状态含义1.2.3 线程状态转换图 2 原子性2.1 volatile关键字2.2 synchronized解决2.3 原子性2.4 AtomicInteger类2.5 悲观锁和乐观锁 前言 …

TikTok五分钟开户快速步骤流程!

1、注册您的账户 首先&#xff0c;访问TikTok广告管理器的注册页面&#xff08;https://ads.tiktok.com/i18n/signup/&#xff09;以创建账户。您可以选择使用电子邮件或手机号码进行注册。输入您的电子邮件和密码后&#xff0c;您需要同意TikTok的广告条款&#xff0c;然后点击…

【路径规划】用于识别封闭多边形竞技场内两点之间的最短路径

摘要 本项目展示了一种在包含障碍物的封闭多边形区域内识别两点之间的最短路径的算法。该算法不依赖于离散化的地图&#xff08;如网格地图&#xff09;&#xff0c;而是直接通过几何计算处理路径和障碍物之间的关系&#xff0c;生成沿着障碍物边缘的最优路径。实验结果表明&a…

软考中级网络工程师选择题

部分参考 软考中级网络工程师全面学习笔记第2版(5万字)配套视频及课件_软考中级网络工程师资料-CSDN博客 1.计算机网络概述 操作系统 OSI七层模型&#xff1a;物联网淑慧试用 TCP/IP&#xff1a;网网&#xff08;网际层&#xff09;传应 高频考点&#xff1a; 中央处理器CP…

数仓思想、数仓建模、维度建模理论、数仓面试题

文章目录 一、数仓建模1、数仓建模的意义2、数仓建模方法论1&#xff09;ER模型&#xff1a;2&#xff09;六范式&#xff1a; 3、维度模型 二、维度建模理论之事实表1、事务型事实表2、周期型快照事实表3、累计型快照事实表 三、维度建模理论之维度表1、设计步骤2、设计要点3、…

C++ 9.25

手动实现栈、和队列 stack #include <iostream> using namespace std; class Stack { private: int* arr; // 存储栈元素的数组 int top; // 栈顶索引 int capacity; // 栈的容量 public: Stack(int size) { arr new int[size]; c…

uni-app+vue3开发微信小程序使用本地图片渲染不出来报错[渲染层网络层错误]Failed to load local image resource

我把图片放在assets里面页面通过相对路径引入。结果一直报错。 最后我把图片放在static文件夹下面。然后修改路径指向static就可以了 或者是我们必须先import 这个图片然后在使用 import banner1 from ../../assets/images/banner/banner1.png; <image :src"banner…