内存管理--《Hello C++ Wrold!》(8)--(C/C++)--深入剖析new和delete的使用和底层实现

article/2025/7/13 16:37:11

文章目录

  • 前言
  • C/C++内存分布
  • new和delete
    • new和delete的底层
    • 定位new表达式
  • 内存泄漏
  • 作业部分

前言

在C/C++编程中,内存管理是理解程序运行机制的核心基础,也是开发高效、稳定程序的关键。无论是局部变量的存储、动态内存的分配,还是对象生命周期的管理,都与内存的合理使用密切相关。

本内容将围绕C/C++内存分布、动态内存操作(new/deletemalloc/free)、内存泄漏等核心概念展开,通过理论解析与实例分析相结合的方式,帮助读者深入理解以下关键内容:

  • 程序运行时内存的逻辑分区(栈区、堆区、静态区、常量区等)及其数据存储特性;
  • new/deletemalloc/free的本质区别,以及在自定义类型场景下的关键差异;
  • 动态内存分配的底层实现原理与定位new表达式的特殊应用;
  • 内存泄漏的成因与典型案例分析,为后续学习智能指针、内存检测工具等进阶内容奠定基础。

此外,内容中还包含典型例题与面试考点,通过具体场景帮助读者强化理解,掌握内存管理的实践技巧与常见问题排查方法。无论是编程初学者还是希望巩固基础的开发者,均可通过本文系统梳理C/C++内存管理的核心知识体系。

C/C++内存分布

在这里插入图片描述

栈区存放局部数据,如函数参数使用的空间

堆区存放动态开辟的空间

静态区存放静态数据/全局变量

常量区存放常量数据

注意:局部的静态变量也存在静态区,const不会改变应该存在哪

判断两个变量所在的区是不是同一个的一个小技巧:看他们的地址相差大不大

new和delete

回顾:malloc,calloc,realloc的区别

malloc就是分配内存

calloc是分配内存加上把内存中的值搞成0

realloc就是扩大或者缩小已经分配的内存

new/delete和malloc/free的区别:(简略)

1.对于动态申请内置类型的数据:

new/malloc除了用法上面,其他方面没有什么区别

2.对于动态申请自定义类型的数据:

new/malloc除了用法上面,还有一个区别就是:new会调用构造函数区进行初始化,delete会进行调用析构函数进行清理

new如果开辟失败的话,会抛异常,需要用try catch去抓获

注意:new和delete malloc和free要配套使用,不能混用;不然有些编译器会报错

引申:抛异常不抓获的话会弹窗报错,抓获的话会温柔些

new/delete和malloc/free的区别(面试常考)(详细):

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在释放空间前会调用析构函数完成空间中资源的清理

这种的记忆方法:记大点(有哪些方面),然后用自己的话说出来(当然也可以先搞个自己的话的模板)

new和delete的用法:
// 申请和释放一个int类型的空间
int* ptr4 = new int;        delete ptr4;
// 申请和释放一个int类型的空间并初始化为10
int* ptr5 = new int(10);        delete ptr5;
// 申请和释放10个int类型的空间
int* ptr6 = new int[10];      delete[] ptr6;
这种要初始化的话,需要int* ptr6 = new int[10]{1,2,3,4};
这样,没初始化的部分会搞成类型的默认值class A
{
public:
A(int a = 0,int b = 0)
: _a(a)
,_b(b){}
private:
int _a;   int_b;
};
main函数里面:A* p = new A(1,2);    delete p;
当然A* p = new A;也可以,因为有默认构造函数
也可以这样:A*p1 = new A[3]{A(1,1),A(2)};这样//缺省的就按默认构造函数来搞了
//这样搞的话,也会让匿名对象的生命周期延长class A
{
public:
A(int a = 0)
: _a(a)
{}
private:
int _a; 
};
单个参数的话,main函数里面这样也行
A*p1 = new A[3]{1,2};

new和delete的底层

new的话是先开空间,再调用构造函数

这里的开空间:里面用了 operator new->operator new的底层又是malloc

delete是先调用析构函数再释放空间

这里的释放空间:里面用了operator delete->它的底层又是free

eg:new[]T这种也类似,无非就是同时对N个对象搞

对这里的析构函数和构造函数使用的理解:

由构造函数生成的那部分才是由析构函数清理的

注意:不能重复delete和free(除非每次搞完都搞成nullptr)

    也不能delete和free一个未初始化过的指针

定位new表达式

用途:一般是配合内存池使用的

使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表用法:class A
{
public:
A(int a = 0)
: _a(a)    {}
private:
int _a;
};A* p1 = (A*)malloc(sizeof(A));//p1指向的还不是对象,因为没执行构造函数
new(p1)A; // 注意:如果A类的构造函数需要传参数时,此处需要传参
//这里不是匿名对象,是用定位new调用的构造函数,也就是显式调用构造函数
p1->~A();//理解
free(p1);
或者用operator new来申请空间.....
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);//这样传参过去也行
p2->~A();//显示调用
operator delete(p2);

本质:其实就是把new和delete拆开来搞了

内存泄漏

内存泄漏非常常见,解决方案分为两种:1、事前预防型。如智能指针等。2、事后查错型。如泄漏检测工具。 – 详细的方案等到以后再讲

作业部分

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在哪里?---->C
staticVar在哪里?---->C
num1 在哪里?---->A!!!
char2在哪里?---->A
*char2在哪里?---->A//首元素的地址,还是在栈
pChar3在哪里?---->A
*pChar3在哪里?---->D//注意和*char的区别,*pChar指向的是常量区那个
ptr1在哪里?---->A
*ptr1在哪里?---->B
注意:eg:*ptr1和&ptr1要区分
sizeof(char2) = 5; strlen(char2) = 4;
//strlen不算'\0'
注意是\0不是/0

在这里插入图片描述

下面有关c++内存分配堆栈说法错误的是(D)
A.对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制
B.对于栈来讲,生长方向是向下的,也就是向着内存地址减小的方向;对于堆来讲,它的生长方向是向上的,是向着内存地址增加的方向增长
C.对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题
D.一般来讲在 32 位系统下,堆内存可以达到4G的空间,但是对于栈来讲,一般都是有一定的空间大小的D:32位系统下,最大的访问内存空间为4G,所以不可能把所有的内存空间当做堆内存使用
C++中关于堆和栈的说法,哪个是错误的:(C)
A.堆的大小仅受操作系统的限制,栈的大小一般较小
B.在堆上频繁的调用new/delete容易产生内存碎片,栈没有这个问题
C.堆和栈都可以静态分配
D.堆和栈都可以动态分配堆只能动态分配,栈可以用函数_alloca进行动态分配
ClassA *pclassa=new ClassA[5];
delete pclassa;
c++语言中,类ClassA的构造函数和析构函数的执行次数分别为(D)
A.5,1
B.1,1
C.5,5
D.程序可能崩溃原因:申请对象数组,会调用构造函数5次,delete由于没有使用[];此时只会调用一次析构函数,但往往会引发程序崩溃
如果是内置类型的话,不会崩溃:eg:
使用 char* p = new char[100]申请一段内存,然后使用delete p释放//也不会有内存泄漏
//因为内置类型不需要调用析构函数
设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为? (ABDC)
C c;
void main()
{A*pa=new A();B b;static D d;delete pa;
}

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

相关文章

Linux之进程间通信

目录 一、进程间通信介绍 1.1、进程间通信目的 1.2、进程间通信发展 1.3、进程间通讯分类 二、管道 三、匿名管道 3.1、示例代码 完整重定向问题: 3.2、⽤ fork 来共享管道原理 3.3、站在⽂件描述符⻆度-深度理解管道 3.4、站在内核⻆度-管道本质 3.5、…

京东轨迹验证码识别代码

一、简介 这个是最新的京东轨迹验证码,需要用户根据轨迹画出对应的曲线。这个和传统的验证码有较大的差异,有非常大的难度。经过长时间的研究,现在终于解决了它的识别问题。 这个是识别效果,和真实轨迹基本上重合,所以…

【Godot】如何导出 Release 版本的安卓项目

在使用 Godot 引擎开发安卓游戏或应用时,发布到应用市场(如 Google Play、华为应用市场等)通常需要生成一个 Release 版本的 .apk 包,而非 Debug 版本。本文将详细介绍如何将 Godot 项目导出为 Release 版本的安卓项目&#xff0c…

哈喽,我是钓鱼的肝

我嘛,一个五年级的小猴子,哦不,小孩子,给大家看看我长什么样子 呃,放错了 别想了,等你关注我再说 我写博客的缘由就是想让大家一起见证我的进步,二是把我的学习成果让更多人看见,三…

【Linux】网络--网络层--IP协议

个人主页~ 网络--网络层--IP协议 一、基本概念二、IP报头格式三、网络划分四、私有IP和公网IP五、路由路由表 六、与数据链路层之间的协议 一、基本概念 IP 协议是用于在互联网上进行数据传输的一种网络层协议,它为不同网络中的设备提供了一种统一的方式来交换数据…

MCP (模型上下文协议):AI界的“USB-C”标准,开启大模型应用新纪元

今天我们来聊聊一个可能深刻改变 AI 应用开发模式的新技术——MCP (Model Context Protocol,模型上下文协议)。这是由 Anthropic 公司(旗下拥有知名大模型 Claude)提出的一项旨在统一大模型与外部世界交互方式的“万能接口”。不妨把它想象成…

Python数学可视化——显函数、隐函数及复杂曲线的交互式绘图技术

Python数学可视化——显函数、隐函数及复杂曲线的交互式绘图技术 一、引言 在科学计算和数据分析中,函数与方程的可视化是理解数学关系和物理现象的重要工具。本文基于Python的Tkinter和Matplotlib库,实现一个功能完善的函数与方程可视化工具&#xff…

SpringBoot(六)--- AOP、ThreadLocal

目录 前言 一、AOP基础 1.入门程序 2. AOP核心概念 3. 底层原理 二、AOP进阶 1.通知类型 抽取切入点 2. 切入点表达式 2.1 execution 2.2 annoation 2.3 连接点详解 三、ThreadLocal 前言 AOP(面向切面编程),面向切面编程实际就…

贪心算法应用:在线租赁问题详解

贪心算法应用:在线租赁问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择,从而希望导致结果是全局最优的算法策略。在线租赁问题(Greedy Algorithm for Online Rentals)是一个经典的贪心算法应用场景,下面我将从多个维度全面…

BA-SAM: 用于 Segment Anything 模型的可扩展偏置模式注意力掩码

概要 在本文中,我们解决了 Segment Anything Model (SAM) 的图像分辨率变化挑战。SAM 以其零样本泛化性而闻名,当面对具有不同图像大小的数据集时,性能会下降。以前的方法倾向于将图像大小调整为固定大小或采用结构修改…

centos8修改IP地址和Hostname

修改ip地址 vim /etc/sysconfig/network-scripts/ifcfg-ens33 BOOTPROTO:设置为 static 表示使用静态 IP 地址。 IPADDR:设置新的 IP 地址。 NETMASK:设置子网掩码。 GATEWAY:设置默认网关(可选,但通常需要…

Python Day40 学习(复习学习日志Day5-7)

重新对信贷数据集进行了填补空缺值的操作 自己写的时候,还是出现了问题: 首先是忘记了要定义一下data, 通过data pd.read_csv(data.csv)可以将读取到的数据保存到变量data中,方便后续进行数据分析。 其次,是漏掉了 c data.col…

QML 粒子系统之Affector

目录 基本示例AffectorAge - 改变特定年龄粒子的属性Attractor - 吸引粒子到指定点Friction - 施加摩擦力Gravity - 模拟重力Wander - 随机游走效果Turbulence - 添加湍流效果 下载链接 接上篇QML 粒子系统 (雪花飘落、爆炸粒子效果),本文继续研究粒子系统中的附属效…

Mac 同时录制系统声音和麦克风声音(OBS + BlackHole 免费版)

🎬 一、你将实现的目标 ✅ 用 OBS 免费录制屏幕时,能同时录到: 🖥 系统播放的声音(比如视频、PPT音效、背景音乐) 🎙 你的麦克风说话声音(讲解或旁白) 🧰…

Pytorch知识点2

Pytorch知识点 1、官方教程2、张量🧱 0、数组概念🧱 1. 创建张量📐 2. 张量形状与维度🔢 3. 张量数据类型➗ 4. 张量的数学与逻辑操作🔄 5. 张量的就地操作📦 6. 复制张量🚀 7. 将张量移动到加速…

使用pandas实现合并具有共同列的两个EXCEL表

表1&#xff1a; 表2&#xff1a; 表1和表2&#xff0c;有共同的列“名称”&#xff0c;而且&#xff0c;表1的内容&#xff08;行数&#xff09;<表2的行数。 目的&#xff0c;根据“名称”列的对应内容&#xff0c;将表2列中的“所处行业”填写到表1相应的位置。 实现代…

【农资进销存专用软件】佳易王农资进出库管理系统:赋能农业企业高效数字化管理

一、软件概述与核心优势 &#xff08;一&#xff09;试用版获取方式 资源下载路径&#xff1a;进入博主头像主页第一篇文章末尾&#xff0c;点击卡片按钮&#xff1b;或访问左上角博客主页&#xff0c;通过右侧按钮获取详细资料。 说明&#xff1a;下载文件为压缩包&#xff…

深入理解AMBA总线(七)AHB设计要点和AHB2APB同步桥设计前言

** 深入理解AMBA总线&#xff08;七&#xff09;AHB设计要点和AHB2APB同步桥设计前言 ** 前面的几篇文章介绍了AHB-lite协议。主要内容其实就是文档的介绍加上我自己的一些理解&#xff0c;希望对大家有帮助。今天这篇文章将带来AHB设计需要注意的一些事项&#xff0c;然后带…

消除F/1噪声

目录 简介 如何测量及规定1/f噪声&#xff1f; 1/f噪声对电路有何影响&#xff1f; 如何消除或降低1/f噪声&#xff1f; 简介 本文阐释1/f噪声是什么&#xff0c;以及在精密测量应用中如何降低或消除该噪声。1/f噪声无法被滤除&#xff0c;在精密测量应用中它可能是妨碍实…

洛谷-P3912素数个数题解

P3912 素数个数 题目描述 求 1 , 2 , ⋯ , N 1,2,\cdots,N 1,2,⋯,N 中素数的个数。 输入格式 一行一个整数 N N N。 输出格式 一行一个整数&#xff0c;表示素数的个数。 输入输出样例 #1 输入 #1 10输出 #1 4说明/提示 对于 40 % 40\% 40% 的数据&#xff0c; …