C++ 中new/delete与malloc/free详解
文章目录
- 前言
- 一、new/delete
- 1. 序言
- 2. 使用方法
- 2.1. new 和 delete 基本语法
- 2.2. new 和 delete 的底层实现原理
- 3. 底层原理
- 3.1. operator new 和 operator delete
- 3.2. new 和 delete 的底层实现原理
- 4. 注意事项
- 5. 总结
- 二、malloc/free
- 1. 序言
- 2. 使用方法
- 2.1. malloc 和 free 基本语法
- 2.2. malloc 和 free 的底层实现原理
- 3. 底层原理
- 3.1. sbrk 函数
- 3.2. 内存块管理
- 3.3. 内存对齐
- 4. 注意事项
- 5. new/delete 与 malloc/free 的区别
- 6. 总结
前言
对C++学习感兴趣的可以看看这篇文章哦:C/C++教程
一、new/delete
1. 序言
在 C++ 的程序设计中,动态内存分配是非常常见的操作。new 和 delete 是 C++ 中提供的动态内存分配运算符,它们可以用于动态分配任意类型的内存,并且不需要显式地指定内存块的大小。
2. 使用方法
2.1. new 和 delete 基本语法
new 和 delete 是 C++ 中的关键字,用于动态分配和释放内存。下面是 new 和 delete 的基本语法:
// 动态分配一个对象
Type* p = new Type;// 释放已经分配的对象
delete p;
其中 Type 表示要动态分配的数据类型,p 是一个指向该数据类型的指针。当执行 new Type 操作时,系统会为该类型的对象分配一块内存,并返回指向这个内存块的指针。当不再需要这个对象时,可以通过 delete 操作来释放这块内存。
如果需要同时分配多个对象,可以使用如下语法:
// 动态分配一个数组
Type* p = new Type[n];// 释放已经分配的数组
delete [] p;
其中 n 表示要分配的对象个数,p 是一个指向 Type 类型数组的指针。
需要注意的是,在使用 new 进行动态内存分配时,如果发生内存不足的情况,则会抛出 std::bad_alloc 异常。因此,在程序中应该对这种异常进行处理。
2.2. new 和 delete 的底层实现原理
new 和 delete 在底层实现上也有一些细节需要注意。在执行 new Type 操作时,实际上是依次进行了以下几个步骤:
- 调用 operator new 函数来申请一块大小为
sizeof(Type)的内存。 - 调用类的构造函数来初始化申请到的内存空间。
- 返回指向申请到的内存空间的指针。
而在执行 delete p 操作时,实际上是依次进行了以下几个步骤:
- 调用类的析构函数来释放对象占用的资源。
- 调用 operator delete 函数来释放对象所占用的内存。
其中 operator new 和 operator delete 都是 C++ 中提供的关键字。operator new 函数负责申请内存,而 operator delete 函数负责释放内存。
需要注意的是,和 malloc/free 不同的是,new/delete 能够调用类的构造和析构函数,并自动计算所需的内存空间大小。这也是使用 new/delete 的一大优势。
3. 底层原理
3.1. operator new 和 operator delete
C++ 中的 operator new 函数和 operator delete 函数是用来动态分配和释放内存的。operator new 函数负责申请内存,而 operator delete 函数负责释放内存。
下面是 operator new 的一种实现方式:
void* operator new(size_t size) {if (size == 0) {size = 1;}void* ptr = malloc(size);if (!ptr) {throw std::bad_alloc();}return ptr;
}
其中 size 表示要申请的内存空间大小。如果该值为 0,则将其设置为 1。然后调用 malloc 函数来申请指定大小的内存空间,如果申请失败,则抛出 std::bad_alloc 异常。
下面是 operator delete 的一种实现方式:
void operator delete(void ptr) noexcept {free(ptr);
}
其中 ptr 是要释放的内存空间指针。这里使用了 noexcept 关键字来表明该函数不会抛出任何异常。
注意,在使用 operator new 和 operator delete 函数时,需要自己负责调用类的构造函数和析构函数。
3.2. new 和 delete 的底层实现原理
new 和 delete 关键字在底层实现上实际上是对 operator new 和 operator delete 函数进行了封装和重载,以方便程序员使用。下面是 new 和 delete 的一种实现方式:
void* operator new(size_t size) {if (size == 0) {size = 1;}void* ptr = malloc(size);if (!ptr) {throw std::bad_alloc();}return ptr;
}void operator delete(void* ptr) noexcept {free(ptr);
}void* operator new[](size_t size) {if (size == 0) {size = 1;}void* ptr = malloc(size);if (!ptr) {throw std::bad_alloc();}return ptr;
}void operator delete[](void* ptr) noexcept {free(ptr);
}
可以看到,new 和 delete 实际上是对 operator new 和 operator delete 进行了重载。
注意,对于复杂数据结构,使用new[] 申请多个内存时,会多申请一块4字节的内存,用于存储当前申请的数量,用于delete[]知道调用几次对象的析构函数,但这个数据对外不可见
4. 注意事项
在使用 new/delete 进行动态内存分配时,需要注意以下几点:
- 内存泄漏:必须及时释放不再使用的内存,否则可能会导致内存泄漏。
- 悬空指针:已经释放的内存块指针不能再被访问,否则可能会导致程序崩溃或出现其他的不可预测的错误。
- 不要重复释放内存:同一个内存块只能被释放一次,否则可能会导致程序崩溃或出现其他的不可预测的错误。
- 多线程环境:在多个线程同时访问同一块内存时,需要采用适当的同步机制来保证线程安全。
5. 总结
在 C++ 编程中,应该根据具体情况选择适当的动态内存分配方式,在使用动态内存分配时应该遵循良好的编程习惯,确保程序的正确性和稳定性。同时,也需要注意避免内存泄漏、悬空指针、重复释放内存等问题,保证程序的健壮性和稳定性。
二、malloc/free
1. 序言
malloc 和 free 是 C 中提供的动态内存分配函数,它们可以用于动态分配任意类型的内存,并且不需要显式地指定内存块的大小
2. 使用方法
2.1. malloc 和 free 基本语法
malloc 和 free 是 C中的函数,用于动态分配和释放内存。下面是 malloc 和 free 的基本语法:
// 动态分配一块内存
Type* p = (Type*)malloc(sizeof(Type));// 释放已经分配的内存
free(p);
其中 Type 表示要动态分配的数据类型,p 是一个指向该数据类型的指针。当执行 malloc(sizeof(Type)) 操作时,系统会为该类型的对象分配一块内存,并返回指向这个内存块的指针。当不再需要这个对象时,可以通过 free 操作来释放这块内存。
需要注意的是,在使用 malloc 进行动态内存分配时,如果发生内存不足的情况,则会返回空指针。因此,在程序中应该对这种情况进行处理。
2.2. malloc 和 free 的底层实现原理
malloc 和 free 在底层实现上也有一些细节需要注意。在执行 malloc(sizeof(Type)) 操作时,实际上是依次进行了以下几个步骤:
- 调用系统函数 sbrk 来扩展程序的数据段。(windows则会调用win API来实现这一功能)
- 将申请到的内存块与已经使用的内存块链接起来。
- 返回指向申请到的内存空间的指针。
而在执行 free(p) 操作时,实际上是依次进行了以下几个步骤:
- 将 p 所指向的内存块标记为未使用。
- 将 p 所指向的内存块与其他未使用的内存块合并起来。
- 如果合并后的内存块没有被占用,则释放该内存块。
需要注意的是,在使用 malloc/free 进行动态内存分配时,需要自己负责调用类的构造函数和析构函数,并且无法计算所需的内存空间大小。
3. 底层原理
3.1. sbrk 函数
sbrk 函数是一个系统调用,用于扩展程序的数据段。在 Linux 系统中,sbrk 函数可以返回当前堆顶部地址,并且可以将堆顶部地址向上或向下移动指定的字节数。
下面是一个简单的示例,演示了如何使用 sbrk 函数来获取当前堆顶部地址:
#include <unistd.h>
#include <iostream>int main() {void* p1 = sbrk(0); // 获取当前堆顶部地址std::cout << "p1 = " << p1 << std::endl;void* p2 = sbrk(1024); // 将堆顶部地址向上移动 1024 字节std::cout << "p2 = " << p2 << std::endl;void* p3 = sbrk(-512); // 将堆顶部地址向下移动 512 字节std::cout << "p3 = " << p3 << std::endl;void* p4 = sbrk(0); // 再次获取当前堆顶部地址std::cout << "p4 = " << p4 << std::endl;return 0;
}
而window系统也有自己的win API可以用于分配堆内存
3.2. 内存块管理
malloc 和 free 在底层实现上还需要进行内存块的管理。在使用 malloc 进行动态内存分配时,系统会为申请的内存块添加一些额外的信息
例如内存块的大小、指向下一个内存块的指针等。这些信息会被保存在内存块的开始位置,并且不会影响用户程序对内存空间的访问。
下面是一个简单的示例,演示了如何通过 malloc 函数获取一块内存,以及内存块中包含的信息:
#include <iostream>
#include <cstdlib>int main() {int* p = (int*)malloc(sizeof(int) * 10); // 动态分配一块内存,可以存放 10 个 int 类型的变量if (!p) {std::cout << "Memory allocation failed" << std::endl;return -1;}std::cout << "Allocate memory at address " << p << std::endl;std::cout << "The size of the memory block is " << sizeof(int) * 10 << " bytes" << std::endl;int* next_p = (int*)(((char*)p) + sizeof(int) * 10);std::cout << "The pointer to the next memory block is " << next_p << std::endl;free(p); // 释放内存return 0;
}
3.3. 内存对齐
在使用 malloc 进行动态内存分配时,需要考虑内存对齐的问题。
所谓内存对齐,就是指将数据类型放置在内存中的地址必须满足一定的条件。
具体来说,每个数据类型都有一个与之相关联的对齐值,这个对齐值通常等于该数据类型的大小(例如 int 的对齐值为 4 字节)。在进行内存分配时,系统会确保申请到的内存块的起始地址是对齐值的整数倍。
下面是一个简单的示例,演示了如何通过 malloc 函数获取一块内存,并展示内存对齐的效果:
#include <iostream>
#include <cstdlib>struct MyStruct {double x;char c;int i;
};int main() {std::cout << "Size of MyStruct is " << sizeof(MyStruct) << " bytes" << std::endl;MyStruct* p1 = (MyStruct*)malloc(sizeof(MyStruct));if (!p1) {std::cout << "Memory allocation failed" << std::endl;return -1;}std::cout << "Allocate memory at address " << p1 << std::endl;char* p2 = (char*)p1;for (int i = 0; i < sizeof(MyStruct); ++i) {std::cout << (int)p2[i] << " ";}std::cout << std::endl;free(p1); // 释放内存return 0;
}
可以看到,在分配 MyStruct 类型的内存时,系统会确保返回的起始地址是 double 类型大小的整数倍。
4. 注意事项
在使用 malloc/free 进行动态内存分配时,需要注意以下几点:
- 内存泄漏:必须及时释放不再使用的内存,否则可能会导致内存泄漏。
- 悬空指针:已经释放的内存块指针不能再被访问,否则可能会导致程序崩溃或出现其他的不可预测的错误。
- 不要重复释放内存:同一个内存块只能被释放一次,否则可能会导致程序崩溃或出现
其他的不可预测的错误。
- 内存越界:访问已经释放的内存块,或者访问超出分配的内存块范围的地址,都可能会导致程序崩溃或出现其他的不可预测的错误。
- 不要混用 malloc/free 和 new/delete:在同一个程序中应该统一使用一种内存分配方式,不要混用 malloc/free 和 new/delete,否则可能会导致内存管理出现问题。
5. new/delete 与 malloc/free 的区别
new 和 delete 是 C++ 中提供的动态内存分配运算符,它们和 malloc/free 在功能上是类似的。
new/delete 的使用方法比 malloc/free 更简单直观。另外,new/delete 还有以下几个优点:
- 类型安全:new/delete 可以根据类型自动计算所需的内存空间大小,无需手动指定。
- 自动调用构造函数和析构函数:new 可以自动调用类的构造函数,delete 可以自动调用类的析构函数,更方便地管理对象的生命周期。
- 支持重载:new/delete 运算符可以被重载,从而可以实现一些特殊的功能。
需要注意的是,在使用 new/delete 进行动态内存分配时,同样可能会出现内存泄漏、悬空指针、内存越界等问题。因此,在编写程序时,必须仔细处理动态内存分配和释放的问题,避免出现以上问题。
6. 总结
在使用动态内存分配的过程中,需要注意内存泄漏、悬空指针、内存越界等问题,同时还需要根据具体情况选择合适的内存分配方式。
相关文章:
C++ 中new/delete与malloc/free详解
文章目录前言一、new/delete1. 序言2. 使用方法2.1. new 和 delete 基本语法2.2. new 和 delete 的底层实现原理3. 底层原理3.1. operator new 和 operator delete3.2. new 和 delete 的底层实现原理4. 注意事项5. 总结二、malloc/free1. 序言2. 使用方法2.1. malloc 和 free 基…...
crm软件哪个好?该如何选择?
crm软件哪个好?该如何选择? 首先我们需要明确一下什么是好的CRM系统,优质的CRM系统应该具备以下优势: 1)提高销售效率:通过CRM系统,销售人员可以跟踪客户互动历史和交易记录,了解客…...
蓝桥杯第22天(Python)(疯狂刷题第5天)
题型: 1.思维题/杂题:数学公式,分析题意,找规律 2.BFS/DFS:广搜(递归实现),深搜(deque实现) 3.简单数论:模,素数(只需要…...
软件测试面试常问的问题有哪些?
互联发展是很快的,每年都会有新语言的诞生。 我干测试已经三年了,主要负责web功能测试,java编写接口自动化,APP功能测试,APP 接口自动化(也是用的java),面过得测试也差不多30个&…...
js之文件信息读取篇高级基础
文章目录js之文件信息读取(FileReader)获取文件相关信息的两种方式js原生拖拽事件js之文件信息读取(FileReader) 首先这里面会讲一些知识点 bolb 对象FileReader对象 let blob new Blob([heewwekgewgwer], { type: text/plain …...
SQL Server的死锁说明
死锁指南一、了解死锁二、检测并结束死锁2.1、可能死锁的资源三、处理死锁四、最大限度地减少死锁4.1、以相同的顺序访问对象4.2、避免事务中的用户交互4.3、保持交易简短且在一个批次中4.4、使用较低的隔离级别4.5、使用基于行版本控制的隔离级别4.6、使用绑定连接4.7、停止事…...
关于#define的一些小知识
目录 一,#define的声明格式: 二,#define宏的作用是为了完成替换 #define的替换规则: 三,#define使用时常犯的错误 四,宏与函数的比较 4.1,什么时候使用宏? 4.1,…...
rabbitmq普通集群与镜像集群搭建
1.准备三台centos7主机,并关闭防火墙与selinux 2.安装rabbitmq环境必备的Erlang(以下所有操作三台主机均需要执行) 执行以下命令下载配置erlang yum源 curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash使用yum命…...
session和jwt哪个更好
session和jwtsession优点缺点jwt优点缺点总结session 优点 原理简单,易于学习。用户信息存储在服务端,可以快速封禁某个用户。 缺点 占用服务端内存,硬件成本高。多进程,多服务器时,不好同步-需要使用第三方缓存&a…...
基于TPU-MLIR实现UNet模型部署-决赛答辩02
队伍:AP0200023 目录 初赛 一、 模型导出优化 1.1 直接倒出原始模型并转换 1.2 导出模型前处理 1.2.1 导出Resize 1.2.2 导出归一化 1.3导出模型后处理 1.3.1导出 Resize 与 1.3.2导出 ArgMaxout 1.3.3导出特征转RGB 复赛 一、 确定baseline 二、优化模…...
Maven高级-分模块开发依赖管理
Maven高级-分模块开发&依赖管理1,分模块开发1.1 分模块开发设计1.2 分模块开发实现1.2.1 环境准备1.2.2 抽取domain层步骤1:创建新模块步骤2:项目中创建domain包步骤3:删除原项目中的domain包步骤4:建立依赖关系步骤5:编译maven_02_ssm项目步骤6:将项目安装本地…...
《安富莱嵌入式周报》第308期:开源带软硬件安全认证的PLC设计,开源功率计,可靠PID实现,PR2机器人设计文件全开源,智能手表设计WASP-OS
周报汇总地址:嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版: https://www.bilibili.com/video/BV1F24y157QE 《安富莱嵌入式周报》第308期:开源带软…...
代码随想录算法训练营第五十六天 | 583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结
583. 两个字符串的删除操作 动规五部曲 1、确定dp数组(dp table)以及下标的含义 dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。 2、确定递推…...
Sip协议
简介 SIP(Session Initiation Protocol,会话初始协议)是一个用于建立、更改和终止多媒体会话的应用 层控制协议,其中的会话可以是 IP 电话、多媒体会话或多媒体会议。SIP 是 IETF 多媒体数据和控 制体系结构的核心协议࿰…...
RandomAccessFile类 断点续传
文章目录学习链接RandomAccessFile构造方法实现的接口DataOutputDataInputAutoCloseable重要的方法多线程读写同一个文件(多线程复制文件)代码1代码2断点续传FileUtils学习链接 RandomAccessFile详解 Java IO——RandomAccessFile类详解 java多线程-断点…...
SpringCloud微服务技术栈的注册中心Eureka
文章目录SpringCloud微服务技术栈的注册中心Eureka简介Eureka特点操作步骤环境准备创建Eureka Server注册服务提供方调用服务消费方总结SpringCloud微服务技术栈的注册中心Eureka 简介 在微服务架构中,服务的数量庞大,而且每个服务可能会有多个实例。此…...
Unity最新热更新框架 hybridclr_addressable
GitHub:YMoonRiver/hybridclr_addressable: 开箱即用的商业游戏框架,集成了主流的开发工具。将主流的GameFramework修改,支持Addressable和AssetBundle,已完善打包工具和流程。 (github.com) # 新增GameFramework Addressables 开箱即用 # 新…...
【c语言】一维数组***特性、存储原理
创作不易,本篇文章如果帮助到了你,还请点赞支持一下♡>𖥦<)!! 主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步! 给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…...
[oeasy]python0133_[趣味拓展]好玩的unicode字符_另类字符_上下颠倒英文字符
另类字符 回忆上次内容 上次再次输出了大红心♥ 找到了红心对应的编码黑红梅方都对应有编码 原来的编码叫做 ascii️ \u这种新的编码方式叫unicode包括了 中日韩字符集等 各书写系统的字符集 除了这些常规字符之外 还有什么好玩的东西呢? 颠倒字符 这个网站可以…...
找凶手,定名次,字符串旋转,杨氏矩阵
1.找凶手问题: //题目名称: //猜凶手 //题目内容: //日本某地发生了一件谋杀案,警察通过排查确定凶手必为4个嫌疑犯的一个。 //以下为4个嫌疑犯的供词: //A说:不是我 //B说:是C //C说ÿ…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
