【 C++ 】C/C++内存管理
前言:
😘我的主页:OMGmyhair-CSDN博客

目录
一、C/C++内存分布
二、C语言中动态内存管理方式:malloc/calloc/realloc/free
malloc:
calloc:
realloc:
free:
三、C++内存管理方式
1.用new/delete操作内置类型
2.用new/delete操作自定义类型
3.operator new和operator delete函数
一、C/C++内存分布

二、C语言中动态内存管理方式:malloc/calloc/realloc/free
malloc:
在c语言中,我们可以使用malloc进行动态申请内存:
int main()
{int* p = (int*)malloc(sizeof(int) * 2);return 0;
}
从上面我们可以看到我们申请了8个字节大小的空间。malloc函数的返回值是void*类型的指针,指向已经开辟好的空间的首地址。我们可以通过强转,转为自己需要的类型。我们用malloc申请到的空间,里面没有初始化,值是不确定的。
如果申请空间失败,将会返回空指针。
calloc:
int main()
{//malloc:int* p = (int*)malloc(sizeof(int) * 2);//calloc:int* pc = (int*)calloc(2, sizeof(int));return 0;
}
在上面代码中,我们用calloc申请了2个大小为int的空间。calloc函数的返回值是void*类型的指针,指向已经开辟好的空间的首地址。它与malloc的区别在于,calloc申请到的空间每个比特位都会初始化为0。

realloc:
int main()
{//malloc:int* p = (int*)malloc(sizeof(int) * 2);//calloc:int* pc = (int*)calloc(2, sizeof(int));//realloc:pc = (int*)realloc(pc, sizeof(int) * 4);return 0;
}
realloc可以用来重新申请空间,第一个参数是原空间的地址,第二个参数是新内存块的大小。
如果在原空间的地址上不能往后继续申请内存(后面的位置被占用了),那么realloc会重新开辟一块新内存空间,将原空间上的数据搬到新内存空间,并且对原空间进行释放。新内存空间对于原空间如果更大了,那么多出来的那部分是没有进行初始化的。
如果第一个参数是空指针,那么此时realloc的作用类似于malloc。
当realloc申请空间失败,会返回空指针,但是原空间依旧有效且数据还在。我们用realloc申请巨大的空间来模拟申请空间失败的情况:
int main()
{int* p = (int*)calloc(2, sizeof(int));cout<<"realloc前p的地址:" << p << endl;int* pp = p;p = (int*)realloc(p, sizeof(int) * 1024*1024*1024*1024);cout <<"realloc后p的地址:" << p << endl;cout << "原空间的第一个数:" << pp[0] << endl;return 0;
}
结果:

所以我们在使用realloc的时候,要小心申请失败的情况:
int main()
{int* p = (int*)calloc(2, sizeof(int));int* ppr = (int*)realloc(p, sizeof(int) * 24);if (ppr != NULL){p = ppr;}return 0;
}
free:

如果ptr没有指向由calloc、realloc、malloc开辟的空间,那么产生的结果是不确定的。
但如果ptr是空指针,不会做任何事情。
需要注意的是,free后不会改变ptr的值,ptr依旧指向那片空间,只是此时你在去使用这块空间是非法的。
此处插播一条知识点,为什么在32位环境下指针大小是4个字节?而在64位环境下指针大小是8个字节?
举个例子,当行李箱上假设有3位密码,每一位的范围是从0~9,我们要用多少位数可以表示全部的密码呢?答案是
一个字节8个比特位。
首先,指针装的是地址,在32位系统下,内存地址空间大小是。当我们表示地址时,每一位的范围是0~1,一共32个比特位,地址也就需要用32个比特位去表示,也就是4个字节。
那么64位环境下就更好理解了。在64位系统下,内存地址空间大小是。当我们表示地址时,每一位的范围是0~1,一共64个比特位,地址也就需要用64个比特位去表示,也就是8个字节。
三、C++内存管理方式
1.用new/delete操作内置类型
int main()
{//动态申请一个int大小的内存空间int* p1 = new int;//动态申请一个int大小的内存空间,并且初始化为1int* p2 = new int(1);//动态申请一个3个int大小的内存空间int* p3 = new int[3];//动态申请一个3个int大小的内存空间,并且初始化为0、1、2int* p4 = new int[3] {0, 1, 2};delete p1;delete p2;delete[]p3;delete[]p4;return 0;
}
当我们申请/释放单个元素的空间时,使用new/delete。当我们申请或者释放连续的空间时,使用new[]或者delete[]。注意要搭配起来使用,如果不搭配使用的后果在文章的后面会讲到。
2.用new/delete操作自定义类型
在这里,new/delete和malloc/free的区别就明显体现出来了。
class A
{
public:A(int a1 = 1, int a2 = 2):_a1(a1),_a2(a2){cout << "A(int a1 = 1, int a2 = 2)" << endl;}~A(){cout << "~A()" << endl;}private:int _a1;int _a2;
};int main()
{cout << "malloc:" << endl;A* ma = (A*)malloc(sizeof(A));cout << "-------------------------------------" << endl;cout << endl << "new:" << endl;A* na = new A;cout << "-------------------------------------" << endl;cout << endl << "free:" << endl;free(ma);cout << "-------------------------------------" << endl;cout << endl << "delete:" << endl;delete na;cout << "-------------------------------------" << endl;return 0;
}
看看运行结果:

可以看到对比malloc,new会去调用自定义类型的构造函数。而对比free,delete会去调用自定义类型的析构函数。
对于内置类型而言new/delete和malloc/free是几乎一样的。
3.operator new和operator delete函数
#define free(p) _free_dbg(p, _NORMAL_BLOCK) 总结一下:


4.new和delete的实现原理
(1)内置类型
如果是对内置类型进行申请空间,new/delete和malloc/free基本类似。不同的地方在于,new/delete申请和释放的是单个元素空间,new[]/delete[]申请和释放的是连续的空间,而且new在申请空间失败时会抛出异常而malloc是返回空指针。
int main()
{//抛异常try{while (1){int* p = new int[1024 * 1024];int* p1 = new int[1024 * 1024];}}catch (const exception& e){cout << e.what() << endl;}return 0;
} 
(2)自定义类型
new和delete对于自定义类型的原理,前面已经描述过,这里就不再赘述。
(2)-1 new [N]的原理
1.调用operator new[]函数,这里的operator new[]其实就是调用operator new实现对N个对象空间申请
2.在申请的空间上进行N次构造函数
(2)-2 delete[]的原理
1.在将要释放的对象空间上调用N个析构函数,实现对N个对象的资源清理。
2.调用operator delete[]释放空间,实际上就是调用operator delete来释放空间。
5.new/delete和new[]/delete[]错误搭配以及使用free进行释放new
(1)内置类型
int main()
{int* p = new int[3];delete p;return 0;
} 这个代码有什么危险吗?会产生内存泄漏吗?
答案是什么都不会发生。
因为new[]归根结底还是malloc,delete归根结底也还是free。所以在这个场景下你甚至还能用free来释放空间(不推荐)。
(2)自定义类型
1.场景1

如果我们用free去释放new的空间,编译器不会报。我们也会发现free不会调用析构函数。如果我们在析构函数中有资源的释放,可能会造成内存泄漏。
2.场景2
class A
{
public:A(int a1 = 1, int a2 = 2):_a1(a1),_a2(a2){cout << "A(int a1 = 1, int a2 = 2)" << endl;}private:int _a1;int _a2;
};int main()
{A* p1 = new A[3];delete p1;return 0;
} 正常通过。(注意和场景3的对比)
3.场景3
class B
{
public:B(int b1 = 1, int b2 = 2):_b1(b1), _b2(b2){cout << "B(int b1 = 1, int b2 = 2)" << endl;}~B(){cout << "~B()" << endl;}
private:int _b1;int _b2;
};int main()
{B* p1 = new B[3];delete p1;return 0;
} 查看运行结果:

对比与场景2,同样是用delete对new[]申请的资源进行释放,为什么B类运行时就产生了崩溃呢?
delete[]与delete的区别
我们通过比较可以发现,B跟A相比多了析构函数。当用new[]申请多个B类对象的空间时,new[]其实会在有析构函数的类前面多开出4个字节的空间来存储对象个数。因为你有析构函数,编译器怕你析构函数中有释放资源的操作,所以会记下对象的个数来多次调用析构函数避免内存泄漏。如果你没有析构函数,编译器认为没有释放资源,也就懒得给你另开空间记个数了:
A类和B类大小一样,都是8个字节,2个int成员变量。我们来看看new[]对A类开辟了多少空间,对B类又开辟了多少空间。

我们都知道不能对申请的空间只释放一部分。当B类有析构时,使用delete进行释放就是只对申请的空间释放一部分,导致了程序崩溃。所以我们在使用new/delete和new[]/delete[]时,一定要搭配使用,以免造成错误。
相关文章:
【 C++ 】C/C++内存管理
前言: 😘我的主页:OMGmyhair-CSDN博客 目录 一、C/C内存分布 二、C语言中动态内存管理方式:malloc/calloc/realloc/free malloc: calloc: realloc: free: 三、C内存管理方式…...
智能客服的演变:从传统到向量数据库的新时代
国产数据库的发展在21世纪初取得了显著的进展。根据不完全统计,目前在国内已有超过300种不同的数据库在案。这一现象在40年前几乎是不可想象的,标志着中国在数据库领域取得了巨大的突破和多样化选择。对于对老一辈的故事或数据库发展史充满兴趣的朋友们&…...
python使用超级鹰识别验证码
1.超级鹰注册 超级鹰: https://www.chaojiying.com/ 注册后购买题分 2.获取要识别的图片 我们以这个附件下载的网页为例: https://gh.lnut.edu.cn/system/_content/download.jsp?urltypenews.DownloadAttachUrl&owner1224556702&wbfileid1504223 点开f12然后刷新几…...
基于YOLO目标检测实现表情识别(结合计算机视觉与深度学习的创新应用)
基于YOLO(You Only Look Once)的目标检测技术实现的表情识别项目是一个结合了计算机视觉与深度学习的创新应用。该项目旨在通过分析人脸图像或视频流中的面部特征来识别七种基本人类情感表达:愤怒(Angry)、厌恶&#x…...
Keil导入包出错
1.菜单栏找不到GD系列? 随便新建一个工程,将project用记事本打开后如图2所示。再将别人给的代码工程用记事本打开,发现别人给的工程少了这两行,所以复制粘贴到别人给的工程记事本中,保存刷新后重新打开,就…...
超声波自动气象站
超声波自动气象站的功能优势可以包括以下几个方面: 高精度测量:超声波自动气象站采用超声波技术进行测量,可以实现高精度的测量结果,能够准确地测量气温、湿度、风速、风向等气象参数。 高可靠性:超声波自动气象站采用…...
Mysql事件操作
查看是否开启事件 SELECT event_scheduler; SHOW VARIABLES LIKE %event_scheduler%; 开启或关闭事件 SET GLOBAL event_scheduler 1; SET GLOBAL event_scheduler on; SET GLOBAL event_scheduler 0; SET GLOBAL event_scheduler off; 创建事件sql CREATE EVENT IF…...
Python必知必会:程序员必须知道的22个Python单行代码!
今天给大家分享24个每个Python程序员都必须知道的单行代码,帮你写出更简洁、更优雅、更高效的代码。 1. 列表推导式 列表推导式(List Comprehensions)可以提供一种简洁的方式创建列表。相较于传统的循环,列表推导式更高效、可读…...
MongoDB 的适用场景
MongoDB 的适用场景 MongoDB 是一种基于文档存储的 NoSQL 数据库,与传统的关系型数据库不同,它使用 JSON 类似的二进制文档格式(BSON)来存储数据,并且具备灵活的文档模型、强大的查询能力和水平扩展性。这些特性使得 …...
汽车EDI:montaplast EDI对接
Montaplast 是一家总部位于德国的全球知名汽车零部件供应商,专注于高精度塑料部件的设计、开发和生产。公司成立于1958年,主要为汽车行业提供轻量化、高性能的塑料解决方案。Montaplast 以其在注塑成型技术、表面处理和装配技术方面的专业能力而著称&…...
【idea】设置文件模板
搜索 File and Code Templates 。 添加模板。 在任意文件目录下右键,new->找到添加的模板。 参考链接: IDEA创建模板文件_edit file templates-CSDN博客...
时间戳和日期相互转换+检验日期合法性功能C语言
H文件 #ifndef _TIME_H_ #define _TIME_H_ #include "config.h" #include "DisplayR300.h" #include "DWIN_Fun.h" #include "DWIN_UI.h" #include <string.h>typedef struct {u16 year; /* 定义时间:年 */u8 month; /* 定义…...
SPIRNGBOOT+VUE实现浏览器播放音频流并合成音频
一、语音合成支持流式返回,通过WS可以实时拿到音频流,那么我们如何在VUE项目中实现合成功能呢。语音合成应用非常广泛,如商家广告合成、驾校声音合成、新闻播报、在线听书等等场景都会用到语音合成。 二、VUE下实现合成并使用浏览器播放代码…...
C#绘制常用工业控件(仪表盘,流动条,开关等)
目录 1,使用Graphics绘制Toggle。 效果: 测试代码: Toggle控件代码: 2,使用Graphics绘制Switch。 效果: 测试代码: Switch控件代码: 3,使用Graphics绘制PanelHe…...
Ps:颜色模型、色彩空间及配置文件
颜色模型、色彩空间和配置文件是处理颜色的核心概念。它们虽然互相关联,但各自有不同的功能和作用。 通过理解这些概念及其关系,Photoshop 用户可以更好地管理和优化图像处理流程,确保颜色在不同设备和应用中的一致性和准确性。 颜色模型 Col…...
llvm后端之td定义指令信息
llvm后端之td定义指令信息 引言1 定义指令2 定义Operand3 定义SDNode4 PatFrags4.1 ImmLeaf4.2 PatLeaf 5 ComplexPattern6 谓词条件7 理解dag 引言 llvm后端通过td定义指令信息,并通过dag匹配将IR节点转换为平台相关的指令。 1 定义指令 td通过class Instructio…...
战地机房集装箱数据中心可视化:实时监控与管理
通过图扑可视化技术实时监控战地机房集装箱数据中心的各项运行指标和环境参数,提高部署效率和设备管理能力,确保数据中心稳定运行。...
Linux入门攻坚——31、rpc概念及nfs和samba
NFS:Network File System 传统意义上,文件系统在内核中实现 RPC:函数调用(远程主机上的函数),Remote Procedure Call protocol 一部分功能由本地程序完成 另一部分功能由远程主机上的 NFS本质…...
内网穿透的应用-本地化部署Elasticsearch平替工具OpenObserve并实现无公网IP远程分析数据
文章目录 前言1. 安装Docker2. Docker镜像源添加方法3. 创建并启动OpenObserve容器4. 本地访问测试5. 公网访问本地部署的OpenObserve5.1 内网穿透工具安装5.2 创建公网地址 6. 配置固定公网地址 前言 本文主要介绍如何在Linux系统使用Docker快速本地化部署OpenObserve云原生可…...
哈希表 and 算法
哈希表: 哈希表(Hash table),也被称为散列表,是一种根据关键码值(Key value)而直接进行访问的数据结构。它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
