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

news2025/6/4 13:06:55

文章目录

  • 前言
  • 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.coloradmin.cn/o/2396951.html

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

相关文章

Java进阶---JVM

JVM概述 JVM作用: 负责将字节码翻译为机器码,管理运行时内存 JVM整体组成部分: 类加载系统(ClasLoader):负责将硬盘上的字节码文件加载到内存中 运行时数据区(RuntimeData Area):负责存储运行时各种数据 执行引擎(Ex…

C++ 检查一条线是否与圆接触或相交(Check if a line touches or intersects a circle)

给定一个圆的圆心坐标、半径 > 1 的圆心坐标以及一条直线的方程。任务是检查给定的直线是否与圆相交。有三种可能性: 1、线与圆相交。 2、线与圆相切。 3、线在圆外。 注意:直线的一般方程是 a*x b*y c 0,因此输入中只给出常数 a、b、…

23. Merge k Sorted Lists

目录 题目描述 方法一、k-1次两两合并 方法二、分治法合并 方法三、使用优先队列 题目描述 23. Merge k Sorted Lists 方法一、k-1次两两合并 选第一个链表作为结果链表,每次将后面未合并的链表合并到结果链表中,经过k-1次合并,即可得到…

Unity + HybirdCLR热更新 入门篇

官方文档 HybridCLR | HybridCLRhttps://hybridclr.doc.code-philosophy.com/docs/intro 什么是HybirdCLR? HybridCLR(原名 huatuo)是一个专为 Unity 项目设计的C#热更新解决方案,它通过扩展 IL2CPP 运行时,使其支持动态加载和…

ElasticSearch迁移至openGauss

Elasticsearch 作为一种高效的全文搜索引擎,广泛应用于实时搜索、日志分析等场景。而 openGauss,作为一款企业级关系型数据库,强调事务处理与数据一致性。那么,当这两者的应用场景和技术架构发生交集时,如何实现它们之…

【C语言极简自学笔记】项目开发——扫雷游戏

一、项目概述 1.项目背景 扫雷是一款经典的益智游戏,由于它简单而富有挑战性的玩法深受人们喜爱。在 C 语言学习过程中,开发扫雷游戏是一个非常合适的实践项目,它能够综合运用 C 语言的多种基础知识,如数组、函数、循环、条件判…

Maven概述,搭建,使用

一.Maven概述 Maven是Apache软件基金会的一个开源项目,是一个有优秀的项目构建(创建)工具,它用来帮助开发者管理项目中的jar,以及jar之间的依赖关系,完成项目的编译,测试,打包和发布等工作. 我在当前学习阶段遇到过的jar文件: MySQL官方提供的JDBC驱动文件,通常命名为mysql-…

Unity 环境搭建

Unity是一款游戏引擎,可用于开发各种类型的游戏和交互式应用程序。它由Unity Technologies开发,并在多个平台上运行,包括Windows、macOS、Linux、iOS、Android和WebGL。Unity也支持虚拟现实(VR)和增强现实(AR)技术,允许用户构建逼…

【入门】【练9.3】 加四密码

| 时间限制:C/C 1000MS,其他语言 2000MS 内存限制:C/C 64MB,其他语言 128MB 难度:中等 分数:100 OI排行榜得分:12(0.1*分数2*难度) 出题人:root | 描述 要将 China…

使用 SASS 与 CSS Grid 实现鼠标悬停动态布局变换效果

最终效果概述 页面为 3x3 的彩色格子网格;当鼠标悬停任意格子,所在的行和列被放大;使用纯 CSS 实现,无需 JavaScript;利用 SASS 的模块能力大幅减少冗余代码。 HTML 结构 我们使用非常基础的结构,9 个 .i…

Spring如何实现组件扫描与@Component注解原理

Spring如何实现组件扫描与Component注解原理 注解配置与包扫描的实现机制一、概述:什么是注解配置与包扫描?二、处理流程概览三、注解定义ComponentScope 四、核心代码结构1. ClassPathScanningCandidateComponentProvider2. ClassPathBeanDefinitionSca…

达梦数据库 Windows 系统安装教程

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C、C#等开发语言,熟悉Java常用开…

【Java EE初阶】计算机是如何⼯作的

计算机是如何⼯作的 计算机发展史冯诺依曼体系(Von Neumann Architecture)CPU指令(Instruction)CPU 是如何执行指令的(重点) 操作系统(Operating System)进程(process) 进程 PCB 中的…

RAG理论基础总结

目录 概念 流程 文档收集和切割 读取文档 转换文档 写入文档 向量转换和存储 搜索请求构建 向量存储工作原理 向量数据库 文档过滤和检索 检索前 检索 检索后 查询增强和关联 QuestionAnswerAdvisor查询增强 高级RAG架构 自纠错 RAG(C-RAG&#xf…

列表推导式(Python)

[表达式 for 变量 in 列表] 注意:in后面不仅可以放列表,还可以放range ()可迭代对象 [表达式 for 变量 in 列表 if 条件]

一天搞懂深度学习--李宏毅教程笔记

目录 1. Introduction of Deep Learning1.1. Neural Network - A Set of Function1.2. Learning Target - Define the goodness of a function1.3. Learn! - Pick the best functionLocal minimaBackpropagation 2. Tips for Training Deep Neural Network3. Variant of Neural…

python打卡训练营打卡记录day43

复习日 作业: kaggle找到一个图像数据集,用cnn网络进行训练并且用grad-cam做可视化 进阶:并拆分成多个文件 数据集来源:Flowers Recognition 选择该数据集原因: 中等规模:4242张图片 - 训练快速但足够展示效…

【QT控件】QWidget 常用核心属性介绍 -- 万字详解

目录 一、控件概述 二、QWidget 核心属性 2.1 核心属性概览 2.2 enabled ​编辑 2.3 geometry 2.4 windowTitle 2.5 windowIcon 使用qrc文件管理资源 2.6 windowOpacity 2.7 cursor 2.8 font ​编辑 2.9 toolTip 2.10 focusPolicy 2.11 styleSheet QT专栏&…

uniapp-商城-77-shop(8.2-商品列表,地址信息添加,级联选择器picker)

地址信息,在我们支付订单上有这样一个接口,就是物流方式,一个自提,我们就显示商家地址。一个是外送,就是用户自己填写的地址。 这里先说说用户的地址添加。需要使用到的一些方式方法,主要有关于地址选择器,就是uni-data-picker级联选择。 该文介绍了电商应用中地址信息处…

【第16届蓝桥杯 | 软件赛】CB组省赛第二场

个人主页:Guiat 归属专栏:算法竞赛 文章目录 A. 密密摆放(5分填空题)B. 脉冲强度之和(5分填空题)C. 25 之和D. 旗帜E. 数列差分F. 树上寻宝G. 翻转硬币H. 破解信息 正文 总共8道题。 A. 密密摆放&#xff0…