【C++】_4.内存分布

2023-10-27

目录

1. C/C++内存分布

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

3.C++内存管理方式

3.1 new / delete 操作内置类型

3.2 new / delete 操作自定义类型

4.operator new 与operator delete函数

5. new 和 delete的实现原理

5.1 内置类型:

5.2 自定义类型

6.定位new表达式(placement-new)

7.总结

7.1 malloc / free 与 new / delete 的区别


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

(1)globalVar 存储在数据段 (2)staticGlobalVar 存储在数据段  (3)staticVar 存储在数据段 

(数据段也称静态区,存储静态变量和全局变量)

(4)localVar 存储在栈区(5)num1 存储在栈区

☆(6)char2 存储在栈区(将常量区"a b c d \0"字符串拷贝给char2)

☆(7)*char2 存储在栈区(数组名表示首元素地址,解引用得到数组首元素)  

☆(8)pchar3 存储在栈区(pchar3 是常量字符串"abcd"首字符的地址,指向常量区)

☆(9)*pchar3 存储在代码段

(代码段也成常量区,存储可执行代码和只读常量)

☆(10)ptr1 存储在栈区(局部变量存储在栈区,但ptr作为指针指向堆区动态开辟的空间

☆(11)*ptr1 存储在堆区

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

C语言主要通过malloc calloc realloc进行动态内存的开辟,free进行动态内存的释放:

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

对于上文代码,不需要手动free str2是因为realloc的原地扩容与异地扩容都会将扩容后的区域的起始地址返回给ptr3,只需要手动free str3即可;

C语言的动态内存管理详见:CSDN

3.C++内存管理方式

C++主要是通过new和delete操作符进行动态内存管理

3.1 new / delete 操作内置类型

    int* p1 = (int*)malloc(sizeof(int));

	int* p2 = new int;     //申请1个int的对象
	int* p3 = new int[5];  //申请5个int的数组
	int* p4 = new int(5);  //申请1个int型对象,初始化为5
	int* p5 = new int[5]{ 1,2,3 };  //C++11支持对数组进行初始化

	free(p1);

	delete p2;
	delete[] p3;
	delete p4;
	delete[] p5;

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

3.2 new / delete 操作自定义类型

class A
{
public:
	A(int a=0)
		:_a(a)
	{
		cout << "A(int a)" <<this<< endl;
	}
	A(const A& a)
	{
		cout << "A(const A& a)" <<this<< endl;
	}
	~A()
	{
		cout << "~A()" <<this<< endl;
	}
private:
	int _a;
};
int main()
{
	//兼容C语言
	A* p1 = (A*)malloc(sizeof(A));
	if (p1 == NULL)
	{
		perror("malloc fail");
		return 0;
	}
	free(p1);//释放空间

	//C++
	A* p2 = new A(0);
	//1.堆上申请空间 
	//2.调用默认构造函数初始化,若没有默认构造函数则需提供初始化赋值
	delete p2;
	//1.调用析构函数清理对象中的资源
	//2.释放空间

	A* p3 = new A[10];
   
	delete[] p3;
    //若要实例化多个对象,则要保证有默认构造函数;
	return 0;
}

(1)new 和 delete 是特意为自定义类型定义的动态开辟空间的,不仅在堆上申请了动态空间,还调用了构造函数初始化与析构函数进行清理操作;

(2)new、delete 和 new[ ]、 delete[ ] 一定要匹配;

(3)申请内存失败的处理方式不同:

    //malloc失败会返回空指针,打印结果为0
	char* p1 = (char*)malloc(1024u * 1024u * 1024u * 2-1);
	//cout << p1 << endl;
	//不可以cout打印,因为cout会将char* 自动识别为字符串进行打印
	printf("%p\n", p1);

	//new失败不需要检查返回值,失败会直接抛异常
	char* p2 = new char[1024u * 1024u * 1024u * 2-1];
	printf("%p\n", p2);

4.operator new 与operator delete函数

(1)调试打开反汇编发现在new进行操作时,会出现 call operator new 指令:

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

operator new: 

 

operator delete: 

实际上,operator new与operator delete底层仍然是通过malloc 与free进行动态内存空间的开辟和释放,而operator new存在的意义在于:

该函数帮助new开辟空间,不直接调用malloc可以封装malloc,为了符合C++new的失败机制:失败抛异常而非返空;

(2)opertaor new 与operator delete 也可以进行重载:

//重载一个类的专属的operator new
struct ListNode
{
	int _val;
	ListNode* _next;
	//内存池供所有ListNode使用
	static allocator<ListNode> alloc;

	//重载一个类专属的operator new 
	void* operator new(size_t n)
	{
		cout << "void operator new->STL内存池allocator申请" << endl;
		void* obj=alloc.allocate(1);
		return obj;
	}
	void operator delete(void* ptr)
	{
		cout << "operator delete->STL内存池allocator申请" << endl;
		alloc.deallocate((ListNode*)ptr,1);
	}
	struct ListNode(int val)
		:_val(val)
		,_next(nullptr)
	{ }
};
allocator<ListNode>ListNode::alloc;
int main()
{
	//频繁的申请ListNode->空间碎片
	//提高效率->申请ListNode时不去malloc而是走自己定制的内存池
	ListNode* node1 = new ListNode(1);
	ListNode* node2 = new ListNode(2);
	ListNode* node3 = new ListNode(3);
	//默认调用全局的operator new函数
	delete node1;
	delete node2;
	delete node3;
	return 0;
}

new等同于调用operator new函数+构造函数,默认情况下使用的是库中全局的operator new函数,这个函数是封装的malloc函数,但每个类都可以实现自己专属的operator new函数,此时在new一个类对象时,就会调用自己实现的operator new。

当我们频繁调用new时,会造成效率低下与空间碎片繁多,此时采取自己定义的operator new去内存池申请空间,就会提高效率。

5. new 和 delete的实现原理

5.1 内置类型:

对于内置类型,new与malloc,delete与free基本相似,不同的是:
(1)new与free申请和释放的是单个元素的空间,new[ ]和delete[ ]申请的是连续空间;

(2)new在申请空间失败时会抛异常,malloc会返回NULL;

5.2 自定义类型

(1)new的原理:

① 调用operator new函数申请空间;

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

(2)delete的原理:

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

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

(3)new T[N] 的原理:

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

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

(4)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-lis是类型的初始化列表

示例:

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

int main()
{
	A* p1 = new A;
	A* p2 = (A*)malloc(sizeof(A));
	if (p2==nullptr)
	{
		perror("malloc fail");
	}
	new(p2)A;
	//new(p2)A(10);
	return 0;
}

使用场景:

定位new表达式并不常用,一般都会配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,就需要使用new的定义表达式显式调用构造函数进行初始化;

7.总结

7.1 malloc / free 与 new / delete 的区别

(1)语法使用的区别:

① malloc / free 是函数,new / delete 是操作符;

② malloc 申请的空间不会初始化,new可以初始化;

③ malloc 申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,在[ ]中注明对象个数即可;

④ malloc的返回值是void*,在使用时必须进行强制类型转换,new不需要,new后跟空间类型;

⑤ malloc申请空间失败时返回NULL,因此使用时必须判空,new不需要,但new需要捕获异常;

(2)本质功能的区别:

申请自定义类型对象时,malloc / free只会开辟空间不会构造函数和析构函数,而new在申请空间后调用构造函数完成对象的初始化,delete在释放完空间前会调用析构函数完成空间中资源的清理;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++】_4.内存分布 的相关文章

  • 无法使用 strptime() 获取秒数

    我收到 YYYY MM DDThh mm ss S Z hh mm 这种格式的日期时间 我正在尝试使用复制该值strptime如下所示 struct tm time 0 char pEnd strptime datetime Y m dT
  • 部署 MVC4 项目时出错:找不到文件或程序集

    过去 我只需使用 Visual Studio 2012 发布到 AWS 菜单项即可部署我的 MVC4 网站 到 AWS Elastic Beanstalk 现在 程序可以在本地编译并运行 但无法部署 从消息来看 它似乎正在寻找不在当前部署的
  • C++:无法使用scoped_allocator_adaptor传播polymorphic_allocator

    我有一个vector
  • 自动从 C# 代码进行调试过程并读取寄存器值

    我正在寻找一种方法来读取某个地址的 edx 注册表 就像这个问题中所问的那样 读取eax寄存器 https stackoverflow com questions 16490906 read eax register 虽然我的解决方案需要用
  • 如何在 Unity 中从 RenderTexture 访问原始数据

    问题的简短版本 我正在尝试访问 Unity 中 RenderTexture 的内容 我一直在使用 Graphics Blit 使用自己的材质进行绘制 Graphics Blit null renderTexture material 我的材
  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • Cygwin 下使用 CMake 编译库

    我一直在尝试使用 CMake 来编译 TinyXML 作为一种迷你项目 尝试学习 CMake 作为补充 我试图将其编译成动态库并自行安装 以便它可以工作 到目前为止 我已经设法编译和安装它 但它编译成 dll 和 dll a 让它工作的唯一
  • 使用 Microsoft Graph API 订阅 Outlook 推送通知时出现 400 错误请求错误

    我正在尝试使用 Microsoft Graph API 创建订阅以通过推送通知获取 Outlook 电子邮件 mentions 我在用本文档 https learn microsoft com en us graph api subscri
  • 跨多个控件共享事件处理程序

    在我用 C 编写的 Windows 窗体应用程序中 我有一堆按钮 当用户的鼠标悬停在按钮上时 我希望按钮的边框发生变化 目前我有以下多个实例 每个按钮一个副本 private void btnStopServer MouseEnter ob
  • C# 中可空类型是什么?

    当我们必须使用nullable输入 C net 任何人都可以举例说明 可空类型 何时使用可空类型 https web archive org web http broadcast oreilly com 2010 11 understand
  • C# 用数组封送结构体

    假设我有一个类似于 public struct MyStruct public float a 我想用一些自定义数组大小实例化一个这样的结构 在本例中假设为 2 然后我将其封送到字节数组中 MyStruct s new MyStruct s
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • 为什么模板不能位于外部“C”块内?

    这是一个后续问题一个答案 https stackoverflow com questions 4866433 is it possible to typedef a pointer to extern c function type wit
  • 初始化变量的不同方式

    在 C 中初始化变量有多种方法 int z 3 与 int 相同z 3 Is int z z 3 same as int z z 3 您可以使用 int z z 3 Or just int z 3 Or int z 3 Or int z i
  • Windows 10 中 Qt 桌面应用程序的缩放不当

    我正在为 Windows 10 编写一个简单的 Qt Widgets Gui 应用程序 我使用的是 Qt 5 6 0 beta 版本 我遇到的问题是它根本无法缩放到我的 Surfacebook 的屏幕上 这有点难以判断 因为 SO 缩放了图
  • 可空属性与可空局部变量

    我对以下行为感到困惑Nullable types class TestClass public int value 0 TestClass test new TestClass Now Nullable GetUnderlyingType
  • 将应用程序从 Microsoft Access 迁移到 VB 或 C#.NET

    我目前正试图说服管理层需要将我们的应用程序之一移植到 NET 该应用程序已经发展成为 Access 中的一个庞然大物 SQL 后端 拥有 700 个链接表 650 个表单 子表单 130 个模块和 850 个查询 我几乎知道这样做的所有主要
  • EPPlus Excel 更改单元格颜色

    我正在尝试将给定单元格的颜色设置为另一个单元格的颜色 该单元格已在模板中着色 但worksheet Cells row col Style Fill BackgroundColor似乎没有get财产 是否可以做到这一点 或者我是否必须在互联
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • 如何使用 ReactiveList 以便在添加新项目时更新 UI

    我正在创建一个带有列表的 Xamarin Forms 应用程序 itemSource 是一个reactiveList 但是 向列表添加新项目不会更新 UI 这样做的正确方法是什么 列表定义 listView new ListView var

随机推荐

  • uni-app开发微信小程序,IOS苹果手机上时间格式化错误显示Invalid Date问题解决方案

    1 问题描述 只在真机IOS上面才会出现 小程序模拟器上的ios是不会出现的 原因是因为时间格式只要带有 符号就会 但有个ios机型却不会出错 我用苹果11测试会出现Invalid Date 有几种情况 例如 1 new Date 2023
  • JVM中的新生代和老年代(Eden空间、两个Survior空间)

    现有的主流JVM分别是HotSpot和JRockit 主要研究对象也是这两个 这篇文章里 我们只研究HotSpot 也就是所谓的Sun JVM 目前阶段 Sun的GC方式主要有CMS和G1两种 考虑到效果和实际应用 这里只介绍CMS CMS
  • 【Linux】NUC977移植使用libmodbus

    nuc977移植使用libmodbus 前言 一 libmodbus库的编译 二 程序的编写运行 最后 前言 相关简介 libmodbus是一个快速 跨平台的Modbus库 目前支持Linux Mac OS X FreeBSD QNX和Wi
  • 神经网络模型的模板(def forward)

    基本的网络构建类模板 from torch import nn import torch nn functional as F class net name nn Module def init self super net name se
  • SpringCloud:初识ES(ElasticSearch)

    1 1 了解ES ElasticSearch 1 1 1 ElasticSearch的作用 ElasticSearch是一款非常强大的开源搜索引擎 具备非常多强大功能 可以帮助我们从海量数据中快速找到需要的内容 例如 在GitHub搜索代码
  • emui微信无法连接服务器,华为EMUI5.0手机收不到微信消息,这里有完美解决方案...

    你有木有被人吐槽过回微信慢 可逆根本没收到消息 到底是哪里出了问题呢 捣鼓了半天 小E终于发现了 原来微信收不到消息竟是因为设置有误 有同遭遇的亲们看这里吧 快快get新技能消除误会吧 请确认以下事项 1 确认已开启通知功能 a 开启微信应
  • Obsidian 入门指引

    Obsidian 入门指引 现在Typora开始收费了 作为一个白嫖党 我肯定是不能接受的 所以我找到了一款免费的Markdown编译管理器 Obsidian 并且他的语法和Typora差不多 之前使用Typora的伙伴们可以很快上手 现在
  • C++构造函数详解:从C++11之前到现代C++

    大家好 我是trueDream 在C 编程中 构造函数是一种用于初始化对象的特殊成员函数 它提供了创建对象和初始化对象成员变量的机制 在C 11之前的标准中 C 语言已经提供了多种构造函数来满足不同的需求 本文将从C 11之前的构造函数开始
  • 【Kubernetes资源篇】Deployment控制器入门实战详解

    文章目录 一 Deployment 高级控制器理论 1 Deployment控制器介绍 2 Deployment工作原理 二 Deployment YAML编写及参数解释 1 整体Deployment YAML资源清单内容 2 核心参数解释
  • Maven存储仓库位置的修改以及修改镜像地址

    一 修改Maven存储仓库 第一步 新建一个repository文件夹 当做仓库用 最好不要在系统盘 例如 E repository 第二步 找到已经安装的Maven路径 在apache maven 3 6 3 conf目录下找到setti
  • C 标准库 - 《stddef.h》

    原文链接 https www runoob com cprogramming c standard library stddef h html 简介 stddef h 头文件定义了各种变量类型和宏 这些定义中的大部分也出现在其它头文件中 库
  • 【C语言】qsort函数的使用和模拟实现

    本篇文章我们来了解一下回C语言中qsort函数的使用方法和模拟实现 这是一个通用性很强而且非常方便的库函数 通过这篇文章希望能让你了解sort函数 目录 一 qsort的介绍 二 qsort函数的使用 1 qsort排序整形 2 qsort
  • Spring + iBATIS完整示例

    最近研究了一下Spring iBATIS 发现看别人的例子是一回事 自己写一个完整的应用又是另外一回事 自己受够了网上贴的一知半解的代码 iBATIS是一个持久化框架 封面了sql过程 虽然sql语句需要自己写 另外 我觉得对于初学者来说
  • CH7-HarmonyOS数据持久化

    文章目录 前言 目标 1 创建Data Ability 创建Data 实现UserDataAbility URI介绍 2 文件存储 打开文件 访问Data 3 关系型数据库 基本概念 数据库的增删改查 数据库谓词的使用 查询结果集的使用 开
  • PCB布局布线规则

    PCB布局布线是否规范直接决定了板子能否正常工作 刚接手画板子的工作在规则这方面确实有所欠缺 于是网上求助的时候发现了这位老哥的博客 一个字 精辟 原文连接 http t csdn cn SVbq0 下面的内容全是从大佬文章中复制的 我这篇
  • A Qualifiers Ranking Rules---The 2023 ICPC Asia Regionals Online Contest (1)

    The following is the current ranking rules for the ICPC Asia EC Online Qualifiers and there will be two online contests
  • 基于51单片机的智能大棚光温控制系统

    目录 文章目录 前言 一 器件 51单片机 1602lcd显示屏 ds18b20温度传感器 继电器 hs0038红外模块 二 部分代码展示 1 头文件 2 main c 3 obj c 总结 前言 这是一个基于c51系列单片机做的智能大棚光
  • 一种处理亿级聚合数据的方法

    本文出自 淘系技术公众号 为本人发表的文章 背景 在电商平台的架构体系中 商品数据是系统正常运转的基石 随着平台的发展 商品数据很容易突破亿级 在电商运营方面 平台通常需要举行各种大促 使用各种营销工具吸引消费者 因此需要对商品进行招商 选
  • Windows10的右键菜单添加“管理员取得所有权”

    以前用盗版系统的时候右键菜单中有个 管理员取得所有权 功能 非常好用 不过正版系统和某些盗版系统中右键是没有这个菜单的 需要按以下方式手动添加 1 新建一个txt文件 将以下内容拷贝到文件中 Windows Registry Editor
  • 【C++】_4.内存分布

    目录 1 C C 内存分布 2 C语言的动态内存管理方式 3 C 内存管理方式 3 1 new delete 操作内置类型 3 2 new delete 操作自定义类型 4 operator new 与operator delete函数 5