【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)

简介: 【C++】深入解析C/C++内存管理:new与delete的使用及原理

一、C/C++中程序内存区域划分

内存区域相关作用:

  • 栈又叫堆栈:非静态局部变量、函数参数、返回值等等,栈是向下增长的
  • 内存映射段时高效的I/O映射方式,用于装载一个共享的动态内存库,用户可以使用系统接口创建共享共享内存,做进程间通信
  • 堆用于程序运行时动态内存分配,堆时可以上增长的
  • 数据段:存储全局数据和静态数据
  • 代码段:可执行的代码、只读常量

在语法上将数据段称为静态区、代码段称为常量区,而以上操作系统的命名。

提出相关思考:

  1. 为什么要分不同的区域
  2. 哪个区域是我们需要重点关注的

回答:

  1. 根据对象不同的生命周期和作用域,分配到不同的区域中,统一管理,高效地对对象进行处理
  2. 堆是我们要需要重点关注的,这是系统留给我们控制的内存,其他系统是自动的

1.1 相关练习测试

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

答案:

  • 选择题:C、C、C、A、A 。A、A、A、D、A、B
  • 填空题:40、5、4、4/8、4、4/8

这里容易混洗的char str1[] ="abcd"const char* str2 ="abcd"。这里str1是个数组将常量拷贝到数组,而str2是直接指向常量区中常量。

二、C语言中动态内存管理方式

C语言中,系统通过一系列函数赋予了我们对堆上空间的控制

void Test ()
{
int* p1 = (int*) malloc(sizeof(int));
free(p1);
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
free(p3 );
}

提出思考:

  1. malloc/calloc/realloc的区别是什么?
  2. 这里使用realloc是否还需要free(p2)
  3. malloc的实现原理?

第一个问题的回答:

对于malloc/calloc/realloc是系统为我们提供在堆上申请空间的途径。在功能上大体是相同的,对于malloccalloc这两个函数,除了参数部分及其是否完成初始化,其他功能是相同的。

relloc比较特别,属于扩容时使用的函数。扩容有两种方式:原地扩容和异地扩容。如果realloc第一个参数部分为空,可以当作malloc使用)。具体还是参考下这篇博客有详细解释内存管理

第二个问题的回答:

由于realloc进行了扩容操作。如果是原地扩容,在原来开辟空间上完成扩容操作,这里p3会同p2指向这块空间,只需要free(p3);如果是异地扩容,将p2空间中数据拷贝一份,在堆上找一块空间充足地方,完成扩容和拷贝操作,p2指向原空间,会被系统自动收回,不需要对p2进行free操作。对此无论是原地还是异地,只需要free(p3)即可

第三个问题的回答:

可以通过该链接进行学习GLibc堆利用入门

三、C++内存管理方式

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

3.1 使用new/delete进行数据操作

3.1.1 new/delete 操作内置类型

int main()
{
  //动态申请一个int类型的空间
  int* ptr1 = new int;
  //动态申请一个int类型的空间并且初始化为10
  int* ptr2 = new int(10);
  //动态申请10个int类型的空间
  int* ptr3 = new int[3];
  //动态申请10个int类型的空间并且完成初始化
  int* ptr4 = new int[10]{ 1,2,3 };//剩下没有明确给值,默认为0
  delete ptr1;
  delete ptr2;
  delete []ptr3;
  delete[]ptr4;
  return 0;
}

注意需要匹配使用new和delete操作符:

  • 申请和释放单个元素的空间:new、delete
  • 申请和释放多个元素的空间:new[]、delete[]

3.1.2 new和delete操作自定义类型

class A
{
public:
  A(int a = 0)
    :_a(a)
  {
    cout << "A():" << this << endl;
  }
  ~A()
  {
    cout << "~A():" << this << endl;
  }
private:
  int _a;
};
int main()
{
  //自定义类型
  A* p1 = (A*)malloc(sizeof(A));
  A* p2 = new A(1);
  free(p1);
  delete p2;
  //内置类型
  int* p3 = (int*)malloc(sizeof(int));
  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;
}

从结果上来看,对于newmalloc最大差别在于对自定义类型除了开辟空间以外,还会调用构造函数和析构函数及其进行良好的初始化和控制。对于malloc而言无法对自定义类型进行好的初始化和控制,只负责开辟内存,除此之外内置类型几乎相同(初始化不同)

对于new优于malloc的几点:

  1. 用法上进行调正,更简洁好用
  2. 可以控制初始化
  3. 对于自定义类型,new可以开空间+构造函数
  4. new配合构造函数,可以更加便捷创建节点等
  5. new失败了以后抛异常,不需要手动检查


【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)https://developer.aliyun.com/article/1617322

相关文章
|
机器学习/深度学习 存储 算法
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
反向传播算法虽是深度学习基石,但面临内存消耗大和并行扩展受限的问题。近期,牛津大学等机构提出NoProp方法,通过扩散模型概念,将训练重塑为分层去噪任务,无需全局前向或反向传播。NoProp包含三种变体(DT、CT、FM),具备低内存占用与高效训练优势,在CIFAR-10等数据集上达到与传统方法相当的性能。其层间解耦特性支持分布式并行训练,为无梯度深度学习提供了新方向。
817 1
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
|
8月前
|
缓存 算法 程序员
C++STL底层原理:探秘标准模板库的内部机制
🌟蒋星熠Jaxonic带你深入STL底层:从容器内存管理到红黑树、哈希表,剖析迭代器、算法与分配器核心机制,揭秘C++标准库的高效设计哲学与性能优化实践。
C++STL底层原理:探秘标准模板库的内部机制
|
存储 缓存 Java
【高薪程序员必看】万字长文拆解Java并发编程!(5):深入理解JMM:Java内存模型的三大特性与volatile底层原理
JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm概念说明主内存所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)工作内存。
377 0
|
安全 算法 网络协议
解析:HTTPS通过SSL/TLS证书加密的原理与逻辑
HTTPS通过SSL/TLS证书加密,结合对称与非对称加密及数字证书验证实现安全通信。首先,服务器发送含公钥的数字证书,客户端验证其合法性后生成随机数并用公钥加密发送给服务器,双方据此生成相同的对称密钥。后续通信使用对称加密确保高效性和安全性。同时,数字证书验证服务器身份,防止中间人攻击;哈希算法和数字签名确保数据完整性,防止篡改。整个流程保障了身份认证、数据加密和完整性保护。
|
11月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
369 26
|
机器学习/深度学习 数据可视化 PyTorch
深入解析图神经网络注意力机制:数学原理与可视化实现
本文深入解析了图神经网络(GNNs)中自注意力机制的内部运作原理,通过可视化和数学推导揭示其工作机制。文章采用“位置-转移图”概念框架,并使用NumPy实现代码示例,逐步拆解自注意力层的计算过程。文中详细展示了从节点特征矩阵、邻接矩阵到生成注意力权重的具体步骤,并通过四个类(GAL1至GAL4)模拟了整个计算流程。最终,结合实际PyTorch Geometric库中的代码,对比分析了核心逻辑,为理解GNN自注意力机制提供了清晰的学习路径。
923 7
深入解析图神经网络注意力机制:数学原理与可视化实现
|
机器学习/深度学习 缓存 自然语言处理
深入解析Tiktokenizer:大语言模型中核心分词技术的原理与架构
Tiktokenizer 是一款现代分词工具,旨在高效、智能地将文本转换为机器可处理的离散单元(token)。它不仅超越了传统的空格分割和正则表达式匹配方法,还结合了上下文感知能力,适应复杂语言结构。Tiktokenizer 的核心特性包括自适应 token 分割、高效编码能力和出色的可扩展性,使其适用于从聊天机器人到大规模文本分析等多种应用场景。通过模块化设计,Tiktokenizer 确保了代码的可重用性和维护性,并在分词精度、处理效率和灵活性方面表现出色。此外,它支持多语言处理、表情符号识别和领域特定文本处理,能够应对各种复杂的文本输入需求。
1617 6
深入解析Tiktokenizer:大语言模型中核心分词技术的原理与架构
|
传感器 人工智能 监控
反向寻车系统怎么做?基本原理与系统组成解析
本文通过反向寻车系统的核心组成部分与技术分析,阐述反向寻车系统的工作原理,适用于适用于商场停车场、医院停车场及火车站停车场等。如需获取智慧停车场反向寻车技术方案前往文章最下方获取,如有项目合作及技术交流欢迎私信作者。
1087 2
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。

推荐镜像

更多
  • DNS