【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

相关文章
|
6月前
|
Web App开发 缓存 监控
内存溢出与内存泄漏:解析与解决方案
本文深入解析内存溢出与内存泄漏的区别及成因,结合Java代码示例展示典型问题场景,剖析静态集合滥用、资源未释放等常见原因,并提供使用分析工具、优化内存配置、分批处理数据等实用解决方案,助力提升程序稳定性与性能。
1724 1
|
12月前
|
机器学习/深度学习 存储 算法
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
反向传播算法虽是深度学习基石,但面临内存消耗大和并行扩展受限的问题。近期,牛津大学等机构提出NoProp方法,通过扩散模型概念,将训练重塑为分层去噪任务,无需全局前向或反向传播。NoProp包含三种变体(DT、CT、FM),具备低内存占用与高效训练优势,在CIFAR-10等数据集上达到与传统方法相当的性能。其层间解耦特性支持分布式并行训练,为无梯度深度学习提供了新方向。
664 1
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
|
6月前
|
弹性计算 定位技术 数据中心
阿里云服务器配置选择方法:付费类型、地域及CPU内存配置全解析
阿里云服务器怎么选?2025最新指南:就近选择地域,降低延迟;长期使用选包年包月,短期灵活选按量付费;企业选2核4G5M仅199元/年,个人选2核2G3M低至99元/年,高性价比爆款推荐,轻松上云。
644 11
|
6月前
|
缓存 算法 程序员
C++STL底层原理:探秘标准模板库的内部机制
🌟蒋星熠Jaxonic带你深入STL底层:从容器内存管理到红黑树、哈希表,剖析迭代器、算法与分配器核心机制,揭秘C++标准库的高效设计哲学与性能优化实践。
C++STL底层原理:探秘标准模板库的内部机制
|
11月前
|
存储 缓存 Java
【高薪程序员必看】万字长文拆解Java并发编程!(5):深入理解JMM:Java内存模型的三大特性与volatile底层原理
JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm概念说明主内存所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)工作内存。
333 0
|
7月前
|
存储 大数据 Unix
Python生成器 vs 迭代器:从内存到代码的深度解析
在Python中,处理大数据或无限序列时,迭代器与生成器可避免内存溢出。迭代器通过`__iter__`和`__next__`手动实现,控制灵活;生成器用`yield`自动实现,代码简洁、内存高效。生成器适合大文件读取、惰性计算等场景,是性能优化的关键工具。
379 2
|
8月前
|
弹性计算 前端开发 NoSQL
2025最新阿里云服务器配置选择攻略:CPU、内存、带宽与系统盘全解析
本文详解2025年阿里云服务器ECS配置选择策略,涵盖CPU、内存、带宽与系统盘推荐,助你根据业务需求精准选型,提升性能与性价比。
|
9月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
303 26
|
9月前
|
存储 弹性计算 固态存储
阿里云服务器配置费用整理,支持一万人CPU内存、公网带宽和存储IO性能全解析
要支撑1万人在线流量,需选择阿里云企业级ECS服务器,如通用型g系列、高主频型hf系列或通用算力型u1实例,配置如16核64G及以上,搭配高带宽与SSD/ESSD云盘,费用约数千元每月。
1160 0

推荐镜像

更多
  • DNS