C++之内存管理

                   

                       

                                         🌹个人主页🌹:喜欢草莓熊的bear

                                                🌹专栏🌹:C++入门

 

目录

前言

一、C/C++内存分配

二、 malloc、calloc、realloc、free

三、C++内存管理方式

3.1 new/delete 操作内置类型

3.2 new和detele操作自定义类型

四、operator new与operator delete函数

4.1 operator new与operator delete函数(重点)

五、new和delete的实现原理

5.1 内置类型

5.2 自定义类型

六、定位new表达式(placement-new) (了解)

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


 

前言

hello ,大家又来跟着bear学习了。一起奔向更好的自己

一、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. 选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?____
staticGlobalVar在哪里?____
staticVar在哪里?____
localVar在哪里?____
num1 在哪里?____
char2在哪里?____
*char2在哪里?___
pChar3在哪里?____
*pChar3在哪里?____
ptr1在哪里?____
*ptr1在哪里?____

 globalvar我们发现他定义成全局变量,全局变量和静态变量一般都放在静态区。所以是选择C。

staticGlobalvar是静态的全局变量所以还是静态区,选C。

staticVar是test函数的静态变量所以还是放在静态区,选C。

localVar定义在test函数的int类型变量,局部变量一般放在栈里面。故选择A。

num1是一个int类型的数组还是局部变量储存在栈里面,选择A。

char2是一个char类型的数组,同理储存在栈里面,选择A。

*char2是对char进行解引用操作,类似于对指针解引用也就是得到char2数组里面的值,因为数组里面的值储存在栈里面。所以*char2还是在栈里面,选择A。

pChar3是const char类型的指针是一个局部变量储存在栈里面,故选择A。

*pChar3是对他进行解引用操作,但是pChar3是指向"abcd"这个地方,我们解引用也就是问"abcd"储存在哪里?(abcd是常量字符串)因为是常量所以储存在常量区也就是代码段里面。故选择D。

ptr1是一个int类型的指针,指针也是局部变量储存在栈里面。所以选择A。

*ptr1对也就是ptr1里面的数据储存在哪里?因为是动态开辟了一块空间。所以数据也储存在这块动态开辟的空间里面。动态开辟所以储存在堆里面。选择B。

 

1. 又叫堆栈 -- 非静态局部变量 / 函数参数 / 返回值等等,栈是向下增长的。
2. 内存映射段 是高效的 I/O 映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
创建共享共享内存,做进程间通信。( Linux 课程如果没学到这块,现在只需要了解一下)
3. 用于程序运行时动态内存分配,堆是可以上增长的。
4. 数据段 -- 存储全局数据和静态数据。
5. 代码段 -- 可执行的代码 / 只读常量。
我自己还有一些理解:
函数调用就是建立了“栈”,动态申请就是在“堆”。
栈:存储局部变量、函数参数和返回地址。
堆:动态分配的内存,由 new 和 delete 操作,还有像什么malloc和free操作的。
静态区:存储全局变量、静态变量。
代码段:用来储存常量区的。

二、 malloc、calloc、realloc、free

1. malloc:
功能:分配指定大小的内存块。
使用: void* malloc(size_t size); 

说明: malloc  分配的内存块中的值是未初始化的,即它们可能包含任意数据。因此,在使用  malloc  分配的内存之前,通常需要使用  memset  或其他方式来初始化内存。
2. calloc:
功能:分配指定数量的元素,每个元素大小为指定大小的内存块,并初始化为零。
使用: void* calloc(size_t num, size_t size); 

说明: calloc  会分配  num * size  字节的内存,并将所有位初始化为零。这在需要分配数组时特别有用,因为它确保了所有的元素都被初始化为零。
3. realloc:
功能:重新分配指定内存块的大小。
使用: void* realloc(void* ptr, size_t new_size); 

说明: realloc  用于调整之前使用  malloc  或  calloc  分配的内存块的大小。如果  ptr  是  NULL ,则  realloc  的行为类似于  malloc 。如果内存块被成功扩展或缩小, ptr  指向的内存块将被更新以反映新的大小。如果  new_size  小于或等于原始大小, realloc  可能不会改变内存块的大小,但仍然会返回指向原始内存块的指针(也就是失败了不改变原来那片空间,只有成功了才会改便之前的空间)。
注意事项:
这些函数返回的指针类型为  void* ,这意味着它们返回一个通用指针,可以被转换为任何类型的指针。
如果内存分配失败, malloc  和  calloc  会返回  NULL ,而  realloc  会返回  NULL  并且不会改变原始的内存块。
使用这些函数分配的内存需要在使用完毕后通过  free  函数释放,以避免内存泄漏。
这些函数是 C 语言中动态内存管理的基础,正确使用它们对于编写高效且稳定的程序至关重要。

malloc的实现原理?glibc中malloc实现原理有兴趣的可以点开看

三、C++内存管理方式

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

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

 

 

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用
new[]和delete[],注意:匹配起来使用。
虽然也可以使用free来释放空间。但是并不建议使用。

3.2 new和detele操作自定义类型

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));
A* p2 = new A(1);
free(p1);
delete p2;
// 内置类型是几乎是一样的
int* p3 = (int*)malloc(sizeof(int)); // C
int* p4 = new int;
free(p3);
delete p4;
A* p5 = (A*)malloc(sizeof(A)*10);
A* p6 = new A[10];
free(p5);
delete[] p6;
return 0;
}

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

四、operator newoperator 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)
{
// try to allocate size bytes
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);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
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 );
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

不用看懂!

五、newdelete的实现原理

5.1 内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申
请空间失败时会抛异常,malloc会返回NULL。

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

六、定位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;
}

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

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.xdnf.cn/news/18412.html

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

相关文章

QT适配最新版Android SDK

从AndroidStudio的SDK管理下载最新版SDK 从https://www.androiddevtools.cn/下载国内安卓SKDTools 这里下载SKDTools后不需要使用SDK Manager.exe下载SDK&#xff08;SDK Manager.exe下载的SDK都是旧版&#xff0c;没法支持新版本&#xff09;&#xff0c;直接使用从AndroidS…

Ubuntu 环境下通过 Apt-get 安装软件

操作场景 为提升用户在云服务器上的软件安装效率&#xff0c;减少下载和安装软件的成本&#xff0c;腾讯云提供了 Apt-get 下载源。在 Ubuntu 环境下&#xff0c;用户可通过 Apt-get 快速安装软件。对于 Apt-get 下载源&#xff0c;不需要添加软件源&#xff0c;可以直接安装软…

反转链表、链表内指定区间反转

反转链表 给定一个单链表的头结点pHead&#xff08;该头节点是有值的&#xff0c;比如在下图&#xff0c;它的val是1&#xff09;&#xff0c;长度为n&#xff0c;反转该链表后&#xff0c;返回新链表的表头。 如当输入链表{1,2,3}时&#xff0c;经反转后&#xff0c;原链表变…

SpringCloud篇(服务网关 - GateWay)

目录 一、简介 二、为什么需要网关 二、gateway快速入门 1. 创建gateway服务&#xff0c;引入依赖 2. 编写启动类 3. 编写基础配置和路由规则 4. 重启测试 5. 网关路由的流程图 6. 总结 三、断言工厂 四、过滤器工厂 1. 路由过滤器的种类 2. 请求头过滤器 3. 默认…

MATLAB实现历史模拟法计算VaR(Value at Risk)

MATLAB实现历史模拟法计算VaR(Value at Risk) 历史模拟法&#xff08;Historical Simulation Method&#xff09;是一种用于计算风险值&#xff08;Value at Risk, VaR&#xff09;的非参数方法。它基于过去的资产价格或收益数据来估计未来的潜在损失。 MATLAB代码如下: 完整…

数据结构——红黑树

目录 一.红黑树 二.红黑树的实现 1.红黑树节点的定义 2.红黑树的插入 3.红黑树的遍历 4.检测红黑树 5.红黑树的查找 6.红黑树的性能 三.整体代码 1.RBTree.h 2.RBTree.cpp 一.红黑树 1.红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上…

OMV7 树莓派 tf卡安装

​ 升级7之后&#xff0c;问题多多&#xff0c;不是docker不行了&#xff0c;就是代理不好使 今天又重装了一遍&#xff0c;用官方的链接&#xff0c;重新再折腾一遍…… 使用raspberry pi imager安装最新版lite OS。 注意是无桌面 Lite版 配置好树莓派初始化设置&#xff0…

Java集合ConcurrentHashMap——针对实习面试

目录 Java集合ConcurrentHashMapConcurrentHashMap的特性是什么&#xff1f;HashMap和ConcurrentHashMap的区别&#xff1f;说说ConcurrentHashMap的底层实现 Java集合ConcurrentHashMap ConcurrentHashMap的特性是什么&#xff1f; 线程安全性 多线程并发读写安全&#xff1a…

攻防世界-mfw

进入题目以后可以点击三个网页&#xff0c;这三个网页其中有一个提示我们git、php、Bootstrap。可以联想到是git泄露 在url后面的参数改为.git 存在git漏洞&#xff0c;这时候就可以利用python的githacker库下载泄漏的文件 查看index.php的源代码 <?phpif (isset($_GET[pa…

MySQL面试之底层架构与库表设计

华子目录 mysql的底层架构客户端连接服务端连接的本质&#xff0c;连接用完会立马丢弃吗解析器和优化器的作用sql执行前会发生什么客户端的连接池和服务端的连接池数据库的三范式 mysql的底层架构 客户端连接服务端 连接的本质&#xff0c;连接用完会立马丢弃吗 解析器和优化器…

代理(下):结构化工具对话、Self-Ask with Search以及Plan and execute代理

在上一讲中&#xff0c;我们深入LangChain程序内部机制&#xff0c;探索了AgentExecutor究竟是如何思考&#xff08;Thought&#xff09;、执行&#xff08;Execute/Act&#xff09;和观察&#xff08;Observe&#xff09;的&#xff0c;这些步骤之间的紧密联系就是代理在推理&…

RPC-路由策略

为什么选择路由策略&#xff1f; 每次上线应用的时候都不止一台服务器会运行实例&#xff0c;那上线就涉及到变更&#xff0c;只要变更就可能导致原本正常运行的程序出现异常&#xff0c;尤其是发生重大变动的时候&#xff0c;导致应用不稳定的因素就变得很多。 灰度发布应用…

ctf日常

8&#xff0c; [NISACTF 2022]easyssrf 跨目录读取 NSSCTF{c42d6e04-f7cb-4ac4-925b-efd9b90c76ff} 9&#xff0c; [SWPUCTF 2021 新生赛]hardrce <?php header("Content-Type:text/html;charsetutf-8"); error_reporting(0); highlight_file(__FILE__); if(is…

Linux笔记---调试工具GDB(gdb)

1. gdb的概念 GDB&#xff0c;全称GNU Debugger&#xff0c;是一个功能强大的开源调试工具&#xff0c;广泛用于Unix和类Unix系统&#xff0c;以及Microsoft Windows和macOS平台。GDB允许开发者在程序执行过程中查看内部运行情况&#xff0c;帮助定位和修复程序中的错误。 gd…

服务架构的演进:从单体到微服务的探索之旅

服务架构的演进&#xff1a;从单体到微服务的探索之旅 一 . 服务架构演变1.1 单体架构1.2 分布式架构1.3 微服务1.4 小结 二 . 微服务技术对比2.1 微服务系统架构图2.2 微服务技术对比2.3 企业需求 三 . Spring Cloud 随着企业业务的不断拓展和复杂度的提升&#xff0c;对软件系…

【论文笔记】LLaMA-VID: An Image is Worth 2 Tokens in Large Language Models

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: LLaMA-VID: An Image is W…

leetcode_二叉树最大深度

对二叉树的理解 对递归调用的理解 对内存分配的理解 基础数据结构&#xff08;C版本&#xff09; - 飞书云文档 每次函数的调用 都会进行一次新的栈内存分配 所以lmax和rmax的值不会混在一起 /*** Definition for a binary tree node.* struct TreeNode {* int val;* …

【模拟仿真】基于区间观测器的故障诊断与容错控制

摘要 本文提出了一种基于区间观测器的故障诊断与容错控制方法。该方法通过构建区间观测器&#xff0c;实现对系统状态的上下边界估计&#xff0c;从而在存在不确定性和外部噪声的情况下进行高效的故障诊断。进一步地&#xff0c;本文设计了一种容错控制策略&#xff0c;以保证…

Ubuntu24.04LTS在线安装Docker引擎

Ubuntu24.04LTS在线安装Docker引擎 文章目录 Ubuntu24.04LTS在线安装Docker引擎1. 官网教程2. 安装Docker引擎1. 卸载旧版本2. 使用存储库apt安装1. 设置Docker的APT仓库(官网)2. 设置Docker的APT仓库(阿里云-推荐) 3. 安装1. 安装最新版2. 安装指定的版本 1. 官网教程 官网安…

directed-map cache简单示例

这张图的地址映射是基于直接映射缓存的策略进行的&#xff0c;以下是详细解释&#xff1a; 直接映射缓存的映射方式 缓存块号 (Cache Block Number): 使用公式 Cache块号 主存块号 % 缓存块总数 来决定主存地址在哪个缓存块中存储。比如&#xff0c;这里 Cache块总数 4&#…