关于C++的动态内存分配

article/2025/9/14 22:02:06

为什么要使用动态内存分配?

比较常见的情况是当我们使用一个数组时,我们需要去声明它,同时我们还需要提供给它一个编译时常量用于指定数组的长度。但是,我们有时候需要的数组并不是定长的。例如,我们要存储一个班级所有学生的数据,但是不同班的学生数量可能是不同的,我们虽然可以声明一个尽量大的数组来存储,但是这样会造成资源的浪费,而且也不利于扩展使用。所以,我们需要动态地去分配一个数组的空间。

malloc和free

在c函数库中有两个函数,malloc和free,在c++中同样是存在的,它们分别用于执行动态内存的分配和释放。这些函数维护一个可用内存池,当一个程序另外一些内存时,就调用malloc函数,malloc从内存池中提取一块合适的内存,并向该程序返回一个指向这块内存的指针。这块内存池并没有以任何形式进行初始化,如果需要被初始化,需要你手动进行初始化或者使用calloc。当此内存不再使用,便调用free函数把它归还给内存池。
函数原型如下:

void *malloc(size_t size);
void *free(void *pointer);

malloc函数的参数就是需要分配的内存字节数,如果内存池中的可用内存可以满足这个需求,则返回一个指向被分配的内存块起始位置的指针。malloc所分配的是一块连续的内存,而且实际分配的内存有可能会比你要求的多一些。
如果内存池的可用内存不满足程序的要求,那么malloc就会向操作系统请求,要求得到更多的内存,并在新内存上执行分配任务。如果操作系统无法提供更多的内存,malloc则返回一个NULL指针。
free函数的参数要么是一个NULL,要么是先前从malloc,calloc,realloc返回的值。(而不能是一个new得到的值),那么这里就有一个问题了,malloc函数指定了size,所以能知道要分配多少内存,free函数没有size,是如何知道它需要释放的内存容量的?其实在每一个内存块的首部都维护着一个mem_control_block(内存控制块),它是一个结构体,形式如下:

struct mem_control_block {//is_available指明此内存块是否可以被使用,1为可以,0为不可以int is_available;//指明当前内存块的大小int size;
};

在使用malloc分配内存时,如果malloc函数内部得到的内存区域的首地址为void *p,那么它返回给你的就是p + sizeof(mem_control_block),偏移到真正的内存块开始的位置,而当我们调用free的时候,则将p - sizeof(men_control_block),回到mem_control_block的头部,这里用*q代替,然后使用q->size,程序就能知道需要释放的内存是多少了。形如下图:
这里写图片描述
另外需要注意的就是free函数释放的是指针所指向的内存块,而不是指针本身,所以我们在释放了内存块之后,还需要将指针指向NULL,否则,就会出现野指针的现象。

calloc和realloc

在C中还有另外两个内存分配函数,calloc和realloc,它们的原型如下:
void *calloc(size_t num_elements,size_t element_size);
void *realloc(void *ptr, size_t new_size);
calloc也用于分配内存,malloc和calloc之间的区别就在于后者在返回指向内存的指针之前把它初始化为0,另外它们之间请求内存的方式也不一样,calloc的参数是所需的元素个数,和每个元素的字节数,根据这个计算出所需的总内存数,然后再去申请。
realloc用于修改一个原先已经分配的内存块的大小,使用这个函数,可以扩大或缩小一个内存块。如果使用它来扩大一个内存块,那么这个内存块原先的内容依然保留,新增加的内存添加到原先内存块的后面,新内存是未被初始化的。如果它被用于缩小一个内存块,则该内存块尾部的部分内存被拿掉,剩余部分内存的原先内容依然保留。
如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新的块上。因此,在使用realloc之后,你就不能再使用指向旧内存的指针,而是应该改用realloc所返回的新指针。
而如果,realloc的第一个参数是NULL,那么它的行为就和malloc一摸一样了。

new和delete

new和delete是C++中动态内存分配的操作符,在我们需要对一些类对象动态地分配内存空间和释放空间的时候,malloc和free就有些不够看了。因为我们为类对象动态分配空间的时候,同时需要调用类对象的构造方法对其进行初始化,释放空间的时候,同样要调用类对象的析构函数,所以我们就需要使用new和delete来做这些工作。
讲到new操作符,就经常牵涉到operator new 和new operator的话题,这两个是什么呢?例如:

int *p = new int;

这样一句简单的代码,我们看到的这个new就是new operator,然而在这句代码的背后,执行的步骤简单描述如下:
1. 为对象分配内存空间;
2. 调用对象的构造函数初始化内存空间;
在第一步中分配内存的部门就使用了operator new,详细的可以参考:c++ 中new 操作符是怎么实现的

new与malloc的区别?

  1. new是一个操作符,而malloc是一个库函数;
  2. new能够自动计算需要分配的内存空间,而malloc需要手工计算字节数;
  3. new计算空间是通过数据类型,而malloc通过字节大小;
  4. new和delete带具有具体类型的指针,而malloc和free则是void *;
  5. new是类型安全的,但是malloc不是,例如int *p = new float[2];这样编译器会报错,但是int *p = malloc(2*sizeof(int));这样是不会报错的;
  6. new操作符可以重载,而malloc不可以;
  7. new可以调用构造函数,但是malloc不可以;

从上面我们可以看出,new操作符的作用要比malloc的作用强大,而且malloc有的功能,使用new也可以做到,那么我们为什么还需要保留malloc呢?答案是在C++中,经常需要外部链接调用C的库,而C中管理动态内存需要使用malloc,所以我们就需要保留下malloc。

内存泄漏以及内存溢出?

什么是内存泄露?
内存泄漏是指内存被动态分配以后,当它不再使用时未被释放。内存泄漏会增加程序的体积,有可能导致程序或系统的奔溃。另外,内存泄漏还有可能导致内存不足的问题。所以,每次动态分配了内存空间的时候,当内存不再需要使用时,都要对其进行释放。
什么是内存溢出?
内存溢出简单理解就是你给一个程序的内存空间(或者你本身所拥有的内存)小于该程序运行现状所需要的内存空间,而且你又没有对这种情况做好处理措施,程序就会操作到本不应该被操作到的内存空间,这样的情况就是溢出现象。这种现象可能会被黑客利用攻击。
更详尽的可以参考:内存泄漏和内存溢出有啥区别?

总结

  1. 在使用malloc函数动态分配内存时,需要检查函数返回的指针是否为NULL;
  2. malloc和free,new和delete要成对使用;
  3. 在动态分配的内存空间不再使用时,要释放该空间;
  4. 释放空间之后,要将指向该内存空间的指针指向NULL,防止出现野指针的情况;
  5. 不要重复释放一块内存,否则可能会破坏自由空间;

参考

  1. https://segmentfault.com/q/1010000000160483
  2. https://www.zhihu.com/question/40560123
  3. 《Effective C++》,《C和指针》,《C++ Primer 第5版》

http://chatgpt.dhexx.cn/article/msTYIO2J.shtml

相关文章

【C语言】动态内存的分配

目录 &#x1f34b;&#x1f34b;前言 &#x1f34b;&#x1f34b;动态内存分配的定义 &#x1f34b;&#x1f34b;动态内存的优势 &#x1f34b;&#x1f34b;<1> 可以控制内存的大小 &#x1f34b;&#x1f34b;<2> 可以多次利用这部分空间 &#x1f34b;…

动态内存分配函数

一、静态存储分配与动态存储分配&#xff1a; &#xff08;1&#xff09;静态存储分配 通常定义变量&#xff08;或对象&#xff09;&#xff0c;编译器在编译时都可以根据该变量&#xff08;或对象&#xff09;的类型知道所需内存空间的大小&#xff0c;从而系统在适当的时候…

内存动态分区分配算法

文章目录 动态分区分配1. 首次适应算法&#xff08;First Fit&#xff09;2. 邻近适应算法&#xff08;Next Fit&#xff09;3. 最佳适应算法&#xff08;Best Fit&#xff09;4. 最坏适应算法&#xff08;Next Fit&#xff09; 总结 动态分区分配 所谓动态分区分配&#xff0…

C++之new动态分配内存生成数组

【任务1】&#xff1a; 编写并测试3*3矩阵转置函数&#xff0c;使用数组保存3*3矩阵。 实验要求 &#xff08;1&#xff09; 转置函数参数为二维数组&#xff1b; &#xff08;2&#xff09; 在main函数中实现输入、输出 #include <iostream> using namespace std…

C语言中的动态内存分配

大家好&#xff0c;今天简单讲一讲C语言中的动态内存分配。 补充&#xff1a;C程序中的内存块。 在C程序中&#xff0c;通常将内存划分为以下六个区域&#xff1a; &#xff08;1&#xff09;内核区域。这块区域是操作系统的&#xff0c;用户不能使用。 &#xff08;2&…

C语言动态分配内存

C语言动态分配内存 malloc 动态开辟内存的函数&#xff1a; void* malloc (size_t size); 这个函数向内存申请⼀块连续可⽤的空间&#xff0c;并返回指向这块空间的指针 如果开辟成功&#xff0c;则返回⼀个指向开辟好空间的指针 如果开辟失败&#xff0c;则返回⼀个NULL…

C/C++动态分配内存的几种方法

使用C/C编程时&#xff0c;会经常动态分配内存&#xff0c;以便合理使用内存&#xff0c;本文主要讲述动态内存分配的几种方法及一些原理&#xff0c;理解不深刻之处欢迎指教。 引言 为什么要进行动态内存分配&#xff1f;以数组为例&#xff0c;数组元素在内存中存储的地址是…

sadad

出版社&#xff1a; 河北少年儿童出版社 ISBN&#xff1a;12176804 版次&#xff1a;1 商品编码&#xff1a;12176804 包装&#xff1a;精装 丛书名&#xff1a; 幼儿家庭课堂 开本&#xff1a;12开 出版时间&#xff1a;2017-05-01 用纸&#xff1a;铜版纸 适读人群 &#xff…

SAADC

电流 :EasyDMA draws about 1.2 mA, and added to the 700 uASAADC current it sums to ~2 mA。和实测差不多 官方例程解析:TIMER + PPI +SAADC。 特征: 1、 支持8/10/12位分辨率,和14位过采样分辨率 2、 8通道:单端输入最多可同时配置8个通道 和 差分输入…

SAZS~

本文提出了一种新颖的形状感知零样本语义分割&#xff08;SAZS&#xff09;框架&#xff0c;利用大规模预训练视觉语言模型特征空间中丰富的先验知识&#xff0c;并通过在边界检测约束任务上进行联合训练来融合形状感知 论文链接&#xff1a;https://arxiv.org/abs/2304.08491…

SASSD

Structure Aware Single-stage 3D Object Detection from Point Cloud 文章&#xff1a;SA-SSD 代码&#xff1a;SA-SSD 这是来自香港理工大学和达摩院的一篇文章&#xff0c;一作是为在读的博士生&#xff0c;何晨航。通讯作者是张磊&#xff0c;香港理工大学的博导&#x…

Sarsa

基本概念 Sarsa Sarsa算法与Q-Learning算法极为相似&#xff0c;‘sarsa’五个字母的含义即s(当前状态)&#xff0c;a(当前行为)&#xff0c;r(奖励)&#xff0c;s(下一步状态)&#xff0c;a(下一步行为)&#xff0c;也就是说我们在进行这一步操作时已经想到当前s对应的a&…

反激电源

扶苗平衡&#xff1a;是针对电感的&#xff0c;不是针对变压器的&#xff1b;是电感稳态期间的情况&#xff0c;不是暂态期间的情况&#xff1b; 一个周期内&#xff0c;电感开通期间两端的电压乘以开通时间电感关断期间两端电压乘以关断时间。 对于反激变压器&#xff0c;其实…

Part-Ⅰ2. 稳态变换器分析原则(二)

2.4 Cuk电路分析 作为第二个示例&#xff0c;变换器可以实现类似buck-boost电路功能&#xff1a;升高或降低电压幅值&#xff0c;并且转换极性。其电路如如下。 该转换器通过电容进行能量传输。当开关打到位置2时&#xff0c;电容 C 1 C_1 C1​通过 L 1 L_1 L1​连到输入电源…

buck-boost基本模型

1.电容模型 2.电感模型 3.伏秒平衡 4.电容电感 5.BUCK 6.boost 7.参考资料 《开关电源的基本概念和分析方法》

2、Principles of Steady-State Converter Analysis

2、Principles of Steady-State Converter Analysis 伏秒平衡与安秒平衡 在电路平衡时&#xff0c;作为存储元件的电感与电容&#xff0c;其储存的能量不可能无穷大&#xff0c;必然会稳在一个稳定值。 稳定后必然有在一个开关周期内有 所以 在求解时&#xff0c;只要根据…

1dB压缩点和三阶交调点、相位差与延时

1dB压缩点与三阶交调点 要知道放大器是一个非线性系统&#xff0c;传输函数基本用泰勒级数表示 如果输入信号幅度很小&#xff0c;那么上式中2次及以上的项就可以忽略而成为小信号的情况。在许多情况下我们可以忽略3次以上的项。 如果输入一个正弦信号 1、可以看到一个单频率…

半桥电路注意事项

应注意的几点问题 偏磁问题 原因&#xff1a;由于两个电容连接点A的电位是随Q1、Q2导通情况而浮动的&#xff0c;所以能够自动的平衡每个晶体管开关的伏秒值&#xff0c;当浮动不满足要求时&#xff0c;假设 Q1、Q2具有不同的开关特性&#xff0c;即在相同的基极脉冲宽度tt1下&…

有关DC/DC和LDO 的详细介绍以及两者的对比----之DC/DC篇

目录 DC/DC工作原理 电感电压伏秒平衡定律 开关电源的三种基本拓扑 同步整流技术 DC/DC电源调制方式 DC/DC芯片的内部构造 DC/DC电路的硬件设计 总结一下DC/DC和LDO的原理&#xff0c;区别和应用&#xff0c;以下内容部分为自己原创&#xff0c;部分在网上找的资料并加上…

BUCK中的电感值---开关电源篇(4)

前一篇&#xff0c;BUCK电路&#xff0c;了解到功率电感中的电流是呈现为以开关频率为周期的伏秒平衡态。 即dI * L dV * dt 1&#xff09; 相同伏秒变化的情况下&#xff0c;电感的值如果越大&#xff0c;则电感上电流的变化量会越小。这个电流的变化量有个专用名称叫纹波电…