C++:内存管理

article/2025/6/8 0:40:53

一.深入理解C/C++的内存分布

以上是一张C/C++ 程序内存分区示意图:

栈区

  存放内容:局部变量(如函数内部定义的普通变量  int a = 10;  )、函数的形式参数 。其特点是由编译器自动分配和释放,遵循先进后出原则,生命周期与函数调用相关,函数调用时分配内存,函数返回时释放内存 。

 

堆区

    存放内容:通过动态内存分配(如C语言中的  malloc  、C++ 中的  new  操作符)获取的内存空间 。例如  int* ptr = new int[10];  ,这10个  int  大小的内存空间就在堆区。堆区内存由程序员手动分配和释放,若不释放会造成内存泄漏 。

 

静态区

   存放内容:全局变量(在函数外部定义的变量 )和静态变量(用  static  修饰的变量,包括静态全局变量和静态局部变量,如  static int b = 20;  ) 。静态区的变量在程序加载时分配内存,程序结束时释放内存,生命周期贯穿整个程序运行过程。

 

练习:

以下是针对代码中各变量存储位置的解析:

1.  globalVar 

 - 解析: globalVar  是全局变量。全局变量在程序启动时就会被分配内存,存储在数据段(静态区)。数据段用于存放已初始化的全局变量和静态变量,其生命周期贯穿整个程序运行过程。

- 答案:C. 数据段(静态区)

 

2.  staticGlobalVar 

- 解析: staticGlobalVar  是静态全局变量。静态全局变量同样在程序启动时分配内存,并且存储在数据段(静态区)。静态全局变量的作用域仅限于定义它的源文件,但其存储特性和全局变量类似,都是在静态存储区域。

- 答案:C. 数据段(静态区)

 

3.  staticVar 

- 解析: staticVar  是函数  Test  中的静态局部变量。虽然它是在函数内部定义,但是由于  static  修饰,它的存储位置不是在栈上,而是在数据段(静态区)。静态局部变量在程序初始化时分配内存,并且在程序运行期间一直存在,其值可以在多次函数调用间保持。

- 答案:C. 数据段(静态区)

 

4.  localVar 

- 解析: localVar  是函数  Test  中的普通局部变量。普通局部变量在函数被调用时在栈上分配内存,当函数执行结束返回时,栈上为该变量分配的内存会被自动释放。栈的操作遵循后进先出(LIFO)原则。

- 答案:A. 栈

 

5.  num1 

- 解析: num1  是函数  Test  中定义的数组,属于局部变量数组。和普通局部变量一样,它在函数调用时在栈上分配内存空间来存储数组元素。当函数返回时,栈上为该数组分配的空间被释放。

- 答案:A. 栈

 

6.  char2 

- 解析: char2  是函数  Test  中的字符数组,也是局部变量。所以它在栈上分配内存来存储字符元素。在函数调用时栈为其分配空间,函数结束时空间被释放。

- 答案:A. 栈

 

7.  *char2 

- 解析: char2  是栈上的字符数组, *char2  表示访问数组的第一个元素,数组整体在栈上,所以其元素也在栈上。

- 答案:A. 栈

 

8.  pChar3 

- 解析: pChar3  是函数  Test  中的指针变量,它是局部变量。局部变量存储在栈上,所以指针变量  pChar3  本身存储在栈上,它存储的是所指向字符串常量的地址。

- 答案:A. 栈

 

9.  *pChar3 

- 解析: pChar3  指向的是字符串常量  "abcd"  ,字符串常量存储在代码段(常量区)。所以通过指针  pChar3  访问到的内容(即  *pChar3  )在代码段(常量区) 。

- 答案:D. 代码段(常量区)

 

10.  ptr1 

- 解析: ptr1  是函数  Test  中的指针变量,它是局部变量。局部变量存储在栈上,所以指针变量  ptr1  本身存储在栈上,它记录的是通过  malloc  在堆上分配内存的地址。

- 答案:A. 栈

 

11.  *ptr1 

- 解析: ptr1  是通过  malloc  函数在堆上分配内存后得到的指针。 *ptr1  表示通过  ptr1  指针访问其所指向的内存区域,这块内存区域是在堆上分配的,所以  *ptr1  指向的内容在堆上。

- 答案:B. 堆

 

二.C语言的动态内存管理

1. 区别:

 ①malloc :按指定字节数分配未初始化内存。

②calloc :按指定数量和单个大小分配内存,且初始化为0。

 ③realloc :调整已有内存块大小,可能移动内存位置。

 

2.是否需  free(p2) :

不需要。 realloc  若重新分配会自动释放  p2  指向内存,若在原地址扩展, p2  和  p3  指向同块内存,只需  free(p3)  。

 

三.C++动态内存管理

C 语言借助  malloc 、 calloc 、 realloc  及  free  等函数管理内存。在 C++ 里,这些方式虽仍可使用,但存在局限且操作繁琐。鉴于此,C++ 推出  new  和  delete  操作符用于动态内存管理。它们能自动适配类型,还会调用构造与析构函数,让对象的创建与释放更便捷、安全 。

1.new/delete操作内置类型

 以上代码展示:

①.  new  操作:

- 单个对象(如  new int 、 new int(3)  ):在堆上分配单个内置类型( int  )空间,带括号可直接初始化值。

- 数组(如  new int[10] {1,2,…}  ):分配连续内置类型数组空间,支持列表初始化,未显式初始化元素用默认值( int  为 0 )。

②.  delete  操作:

- 单个对象( delete  ):匹配  new  分配的单个对象,释放对应堆空间。

- 数组( delete[]  ):匹配  new[]  分配的数组,逐个释放数组元素空间,需严格与  new[]  配对,保障内存正确释放。

对内置类型的操作malloc/new,new/delete作用基本相同

 2.new/delete操作内置类型

 在 C++ 里, new / delete  操作自定义类型(如类  A  )时,与  malloc / free  有本质差异,核心在构造、析构函数的处理。

用  new  创建自定义对象(如  A* p2 = new A(1);  ),会先在堆上开辟存对象的空间,再自动调用构造函数(如  A(int a = 0)  )。构造函数初始化成员(给  _a  赋值 ),执行创建逻辑,让对象合法可用,完成“从无到合规”的过程。

 delete  操作对象(如  delete p2;  )时,先调用析构函数( ~A()  )。析构函数清理资源(类  A  虽简单,若有动态内存等,就负责释放 ),收尾后释放  new  开辟的堆空间,保障资源回收,避免泄漏。

而  malloc (如  A* p1 = (A*)malloc(sizeof(A));  )仅申请堆内存,不调构造函数,成员可能未初始化,对象创建不完整; free(p1)  只释内存,不调析构函数,若对象持资源(文件句柄等 ),会引发泄漏。

所以,对自定义类型, new / delete  借构造、析构自动调用,完善对象生命周期管理,是 C++ 面向对象内存管理关键,让自定义类型使用更安全规范 。

 

四.operator new与operator   delete

以下从概念层面,脱离代码解析  operator new  和  operator delete :
 
1、本质定位
 
 operator new  与  operator delete  是 C++ 内存管理的底层基石函数。
 
-  operator new  专职负责“从系统获取原始内存块”,只做内存分配,不关心对象构造逻辑;
-  operator delete  专职负责“回收原始内存块”,只做内存释放,不涉及对象析构逻辑 。
 
2、与  new / delete  运算符的协作
 
-  new  运算符:创建对象时,先调用  operator new  拿到内存,再自动触发构造函数,完成对象初始化(把“原始内存”变成“可用对象” )。
-  delete  运算符:销毁对象时,先调用析构函数清理对象资源,再调用  operator delete  归还内存(把“对象”还原为“原始内存”并释放 )。
二者配合,让  new / delete  能完整管控对象“创建 - 使用 - 销毁”的生命周期。
 
3、与  malloc / free  的关联
 
- 默认行为:标准库中, operator new  内部默认调用  malloc  实际分配内存; operator delete  内部默认调用  free  释放内存,是 C++ 对 C 内存管理的兼容。
- 自定义拓展:可重写  operator new / operator delete ,脱离  malloc / free 。比如让内存从自定义内存池分配、添加内存统计/调试功能,灵活适配复杂需求。
 
4、重载与定制能力
 
- 全局重载:重写全局的  operator new / operator delete ,会改变整个程序的内存分配规则,所有  new / delete  都会受影响。
- 类专属重载:只为特定类定制,让该类对象的  new / delete  走专属逻辑(如特殊内存策略 ),不干扰其他类。
 
6、异常与安全(了解)
 
-  operator new :默认分配失败抛  std::bad_alloc  异常;也可选“不抛异常”模式,失败返回  nullptr ,方便不同场景容错。
-  operator delete :释放逻辑简单,默认不抛异常,但传入无效指针(如野指针 )会触发未定义行为,需调用者保证指针合法性。
 
简言之, operator new / operator delete  是 C++ 内存管理的“底层通道”,向上支撑  new / delete  运算符的对象完整生命周期管理,向下兼容 C 的  malloc / free ,还能通过自定义满足多样化内存需求,是理解 C++ 内存机制的关键环节。

 

五.new和delete的实现原理

(1)new 的核心流程

1. 内存分配:通过调用  operator new  函数从堆上申请足够的原始内存空间,这一过程类似  malloc ,但  operator new  内部默认会调用  malloc (可自定义实现)。

2. 对象构造:在分配好的内存上,自动调用目标类型的构造函数,完成对象成员的初始化和资源配置,使对象处于合法可用状态。例如创建类  A  的对象时, new A  会先分配内存,再调用  A  的构造函数。

 

(2)delete 的核心流程

1. 对象析构:先调用目标对象的析构函数,释放对象持有的资源(如动态分配的内存、文件句柄等),清理对象状态。

2. 内存释放:通过调用  operator delete  函数释放之前分配的内存, operator delete  内部默认调用  free (同样可自定义),将内存归还给系统,避免内存泄漏。

 

(3)与 malloc/free 的本质区别

1.  malloc/free  仅负责内存的分配与释放,不涉及对象的构造和析构,无法处理自定义类型的资源管理,对象可能处于未初始化或资源未释放的状态。

2.  new/delete  通过  operator new/delete  结合构造/析构函数,形成“内存分配+对象初始化”“对象清理+内存释放”的完整生命周期管理,是 C++ 面向对象特性在内存管理中的体现。

 

(4)底层函数的角色

 operator new/delete  是  new/delete  运算符的底层实现基础,负责纯内存操作; new/delete  则在此之上封装了对象生命周期的管理逻辑,二者结合实现了安全的动态内存管理。

六.malloc/free与new/delete对比

 


http://www.hkcw.cn/article/kiEIVivkwq.shtml

相关文章

【结构型模式】装饰器模式

文章目录 装饰器模式装饰器模式当中的角色和职责装饰器模式的代码实现装饰器模式与代理模式有何不同?装饰器模式的优缺点适用场景 装饰器模式 装饰器模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,对于…

Ubuntu 挂载新盘

1.磁盘分区 rootljz:/# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT loop0 7:0 0 4K 1 loop /snap/bare/5 loop1 7:1 0 104.2M 1 loop /snap/core/17200 loop2 7:2 0 73.9M 1 loop /snap/core22/1908 loop3 7:3 0 104.6M 1 loo…

Flink03-学习-套接字分词流自动写入工具

上一节中通过如下命令启动服务摸来模拟Socket流。 现在我们写一个ServerSocket来模拟让流自动写入不用手动操作。 pom.xml和上一节一致不需要修改 编写代码 同样适用Socket流 // 使用socket流创建一个从 socket 读取文本的数据流,以换行符 \n 作为分隔符DataStre…

2022年 国内税务年鉴PDF电子版Excel

2022年 国内税务年鉴PDF电子版Excelhttps://download.csdn.net/download/2401_84585615/89784658 https://download.csdn.net/download/2401_84585615/89784658 2022年国内税务年鉴是对中国税收政策、税制改革和税务管理实践的全面总结。这份年鉴详细记录了中国税收系统的整体状…

Gitee Wiki:以知识管理赋能 DevSecOps,推动关键领域软件自主演进

关键领域软件研发中的知识管理困境 传统文档管理模式问题显著 关键领域软件研发领域,传统文档管理模式问题显著:文档存储无系统,查找困难,降低效率;更新不及时,与实际脱节,误导开发&#xff1…

Hadoop 3.x 伪分布式 8088端口无法访问问题处理

【Hadoop】YARN ResourceManager 启动后 8088 端口无法访问问题排查与解决(伪分布式启动Hadoop) 在配置和启动 Hadoop YARN 模块时,发现虽然 ResourceManager 正常启动,JPS 进程中也显示无误,但通过浏览器访问 http://主机IP:8088 时却无法打…

【最小生成树】P2573 [SCOI2012] 滑雪

题目 洛谷:P2573 [SCOI2012] 滑雪 分析 题目条件要点分析: 这道题要求 i 能到达 j 的前提是 i 、j 之间有一条连通的边并且i 的高度比 j 高。这意味着本题给出的是一个有向图。时间胶囊可以返回到上一个景点,可以无限使用,意…

2.2.2 06年T2

Stratford的两大对立力量:令人讽刺的居民与令人同情的公司 - 2006年考研英语Text 2精析 本文解析2006年考研英语Text 2,揭示Stratford小镇居民与皇家莎士比亚剧团(RSC)的深层矛盾。 一、原文与翻译 Paragraph 1:对立双方的形成 L1: Stratfor…

基于人工智能算法实现的AI五子棋博弈

1. 项目概述 本项目实现了一个完整的五子棋游戏系统,包含游戏界面、交互逻辑和人工智能对战功能。 系统采用Python语言开发,使用Pygame库进行图形界面渲染,实现了三种游戏模式:人人对战、人机对战和AI对战。 AI算法基于博弈树搜…

在 Ubuntu 系统上使用 Python 的 Matplotlib 库时遇到的字体缺失问题

报错问题 findfont: Font family [SimHei] not found. Falling back to DejaVu Sans. 在现实图片时尝试显示中文字符命令行报错,在图片中显示方框。 最终解决方案 在尝试了各种方法之后,在代码中添加下图中选中行,问题直接解决。

webstrom中git插件勾选提交部分文件时却出现提交全部问题怎么解决

原因是我有个.husky的文件制定了执行提交的时候就是提交所有的文件 修改.husky/pre-commit文件就可以啦 #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh"# 获取通过 WebStorm 提交的暂存文件(仅勾选的部分) STAGED_FILES$(gi…

KINGCMS被入侵

现象会强制跳转到 一个异常网站,请掉截图代码. 代码中包含经过混淆处理的JavaScript,它使用了一种技术来隐藏其真实功能。代码中使用了eval函数来执行动态生成的代码,这是一种常见的技术,恶意脚本经常使用它来隐藏其真实目的。 这段脚本会检…

CMS32M65xx/67xx系列CoreMark跑分测试

CMS32M65xx/67xx系列CoreMark跑分测试 1、参考资料准备 1.1、STM32官方跑分链接 1.2、官网链接 官方移植文档,如下所示,点击红框处-移植文档: A new whitepaper and video explain how to port CoreMark-Pro to bare-metal 1.3、测试软件git下载链接 …

Vue.js教学第十八章:Vue 与后端交互(二):Axios 拦截器与高级应用

Vue 与后端交互(二):Axios 拦截器与高级应用 在上一篇文章中,我们学习了 Axios 的基本用法,包括如何发送不同类型的 HTTP 请求以及基本的配置选项。本文将深入剖析 Axios 的拦截器功能,探讨请求拦截器和响应拦截器的作用、配置方法和应用场景,通过实例展示如何利用拦截…

【信创-k8s】海光/兆芯+银河麒麟V10离线部署k8s1.31.8+kubesphere4.1.3

❝ KubeSphere V4已经开源半年多,而且v4.1.3也已经出来了,修复了众多bug。介于V4优秀的LuBan架构,核心组件非常少,资源占用也显著降低,同时带来众多功能和便利性。我们决定与时俱进,使用1.30版本的Kubernet…

【判断酒酒花数】2022-3-31

缘由对超长正整数的处理&#xff1f; - C语言论坛 - 编程论坛 void 判断酒酒花数(_int64 n) {//缘由https://bbs.bccn.net/thread-508634-1-1.html_int64 t n; int h 0, j 0;//while (j < 3)h t % 10, t / 10, j;//整数的个位十位百位之和是其前缀while (t > 0)h t…

oauth2.0

OAuth 2.0 的工作原理和流程。 OAuth 2.0 是一个授权框架&#xff0c;它允许第三方应用获取对用户资源的有限访问权限&#xff0c;而无需获取用户的密码。以下是详细说明&#xff1a; 1. OAuth 2.0 的四个主要角色 资源所有者&#xff08;Resource Owner&#xff09; 通常是…

笔记本/台式C盘扩容:删除、压缩、跨分区与重分配—「小白教程」

删除C盘右侧分区以扩展 删除分区&#xff0c;也会删除分区中所有资料&#xff0c;请注意备份所有重要资料。 1.WinX选择磁盘管理&#xff0c;右键点击C盘右侧分区&#xff0c;选择删除卷&#xff0c;原分区会变成黑色的“未分配”空间&#xff1b; 2.此时右键C盘选择“扩展卷…

【Bluedroid】蓝牙启动之sdp_init 源码解析

本文围绕Android蓝牙协议栈中 SDP&#xff08;Service Discovery Protocol&#xff0c;服务发现协议&#xff09;模块的初始化函数sdp_init展开&#xff0c;结合核心控制块tSDP_CB及关联数据结构&#xff08;如tL2CAP_CFG_INFO、tCONN_CB等&#xff09;的定义与协作逻辑&#x…

C++学习-入门到精通【13】标准库的容器和迭代器

C学习-入门到精通【13】标准库的容器和迭代器 目录 C学习-入门到精通【13】标准库的容器和迭代器一、标准模板库简介1.容器简介2.STL容器总览3.近容器4.STL容器的通用函数5.首类容器的通用typedef6.对容器元素的要求 二、迭代器简介1.使用istream_iterator输入&#xff0c;使用…