当前位置: 首页 > news >正文

<C++> 内存管理

1.C/C++内存分布

让我们先来看看下面这段代码

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

以下是各个部分分别存储在内存中的哪一个区域

在这里插入图片描述

说明:

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

顺便提一下:为什么说栈是向下增长的,而堆是向上增长的?

简单来说,在一般情况下,在栈区开辟空间,先开辟的空间地址较高,而在堆区开辟空间,先开辟的空间地址较低。

注意:在堆区开辟空间,后开辟的空间地址不一定比先开辟的空间地址高。因为在堆区,后开辟的空间也有可能位于前面某一被释放的空间位置。

2.C++动态内存管理方式

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

new/delete操作内置类型

一、动态申请单个某类型的空间

//动态申请单个int类型的空间
int* p1 = new int; //申请delete p1; //销毁

其作用等价于:

//动态申请单个int类型的空间
int* p2 = (int*)malloc(sizeof(int)); //申请free(p2); //销毁

二、动态申请多个某类型的空间

//动态申请10个int类型的空间
int* p3 = new int[10]; //申请delete[] p3; //销毁

其作用等价于:

//动态申请10个int类型的空间
int* p4 = (int*)malloc(sizeof(int)* 10); //申请free(p4); //销毁

三、动态申请单个某类型的空间并初始化

//动态申请单个int类型的空间并初始化为10
int* p5 = new int(10); //申请 + 赋值delete p5; //销毁

其作用等价于:

//动态申请一个int类型的空间并初始化为10
int* p6 = (int*)malloc(sizeof(int)); //申请
*p6 = 10; //赋值free(p6); //销毁

四、动态申请多个某类型的空间并初始化

//动态申请10个int类型的空间并初始化为0到9
int* p7 = new int[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //申请 + 赋值delete[] p7; //销毁

其作用等价于:

//动态申请10个int类型的空间并初始化为0到9
int* p8 = (int*)malloc(sizeof(int)* 10); //申请
for (int i = 0; i < 10; i++) //赋值
{p8[i] = i;
}free(p8); //销毁

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用 new[]和delete[],注意:匹配起来使用。

new/delete操作自定义类型

有以下类:

class A {
public:A(int a = 0): _a(a) {cout << "A():" << this << endl;}~A() {cout << "~A():" << this << endl;}private:int _a;
};

一、动态申请单个类

A *p1 = new A();//动态申请单个类
delete p1;//等价于
A *p2 = (A *) malloc(sizeof(A));
free(p2);

二、动态申请多个类

A *p5 = new A[10];//动态申请多个类
delete[] p5;//等价于
A *p6 = (A *) malloc(sizeof(A) * 10);
free(p6);

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc和free不会。

总结一下:

1、C++中如果是申请内置类型的对象或是数组,用new/delete和malloc/free没有什么区别。
 2、如果是自定义类型,区别很大,new和delete分别是开空间+构造函数、析构函数+释放空间,而malloc和free仅仅是开空间和释放空间。
 3、建议在C++中无论是内置类型还是自定义类型的申请和释放,尽量都使用new和delete。

3.operator new和operator delete函数

newdelete是用户进行动态内存申请和释放的操作符,operator newoperator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。

int* p1 = (int*)operator new(sizeof(int)* 10); //申请
operator delete(p1); //销毁//等价于
int* p2 = (int*)malloc(sizeof(int)* 10); //申请
free(p2); //销毁

operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。

在这里插入图片描述

注意:虽然说operator newoperator delete是系统提供的全局函数,但是我们也可以针对某个类,重载其专属的operator new和operator delete函数,进而提高效率。

4.new和delete的实现原理

内置类型

如果申请的是内置类型的空间,new/delete和malloc/free基本类似,不同的是,new/delete申请释放的是单个元素的空间,new[]/delete[ ]申请释放的是连续的空间,此外,malloc申请失败会返回NULL,而new申请失败会抛异常。

自定义类型

new的原理
 1、调用operator new函数申请空间。
 2、在申请的空间上执行构造函数,完成对象的构造。

delete的原理
 1、在空间上执行析构函数,完成对象中资源的清理工作。
 2、调用operator delete函数释放对象的空间。

new T[N]的原理
 1、调用operator new[ ]函数,在operator new[ ]函数中实际调用operator new函数完成N个对象空间的申请。
 2、在申请的空间上执行N次构造函数。

delete[ ] 的原理
 1、在空间上执行N次析构函数,完成N个对象中资源的清理。
 2、调用operator delete[ ]函数,在operator delete[ ]函数中实际调用operator delete函数完成N个对象空间的释放。

5.定位new表达式

定位new表达式是C++中的一种高级用法,允许你在特定的内存位置上创建对象,而不是在默认的堆内存上分配。这在某些特殊情况下很有用,例如在特定内存区域上实现内存池或在共享内存上创建对象等。

示例:

#include <iostream>class MyClass {
public:MyClass(int value) : data(value) {std::cout << "构造函数: " << data << std::endl;}~MyClass() {std::cout << "析构函数: " << data << std::endl;}private:int data;
};int main() {// 分配一块内存void* memory = operator new(sizeof(MyClass));// 在预分配的内存上使用定位new表达式创建对象MyClass* obj = new (memory) MyClass(42);// 手动调用析构函数obj->~MyClass();// 释放预分配的内存operator delete(memory);return 0;
}

注意:在未使用定位new表达式进行显示调用构造函数进行初始化之前,operator new申请的空间还不能算是一个对象,它只不过是与A对象大小相同的一块空间,因为构造函数还没有执行。

6.常见问题

malloc/free和new/delete的区别?

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。

不同的地方是:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可, 如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需 要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成 空间中资源的清理

什么是内存泄漏,内存泄漏的危害?

什么是内存泄漏: 内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

内存泄漏的危害: 长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

void MemoryLeaks() {// 1.内存申请了忘记释放int *p1 = (int *) malloc(sizeof(int));int *p2 = new int;// 2.异常安全问题int *p3 = new int[10];Func();// 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.delete[] p3;
}

内存泄漏分类

C/C++程序中一般我们关心两种方面的内存泄漏:

  • 堆内存泄漏(Heap leak)

堆内存指的是程序执行中依据须要分配通过malloc/calloc/realloc/new等从堆中分配的一 块内存,用完后必须通过调用相应的free或者delete删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak

  • 系统资源泄漏

指程序使用系统分配的资源,比如套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

如何避免内存泄漏?

1、工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记住匹配的去释放。
2、采用RAII思想或者智能指针来管理资源。
3、有些公司内部规范使用内部实现的私有内存管理库,该库自带内存泄漏检测的功能选项。
4、出问题了使用内存泄漏工具检测。

delete删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak

  • 系统资源泄漏

指程序使用系统分配的资源,比如套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

如何避免内存泄漏?

1、工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记住匹配的去释放。
2、采用RAII思想或者智能指针来管理资源。
3、有些公司内部规范使用内部实现的私有内存管理库,该库自带内存泄漏检测的功能选项。
4、出问题了使用内存泄漏工具检测。

相关文章:

<C++> 内存管理

1.C/C内存分布 让我们先来看看下面这段代码 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";char *pChar3 "abcd";int *ptr1 (int *) mal…...

【Java】ByteBuffer类的arrayOffset方法详解+示例

arrayOffset功能详解;arrayOffset在position等于0和非0两种场景下的demo。使用类java.nio.ByteBuffer中的arrayOffset()方法可以获得这个缓冲区的第一个元素在底层支持(backing)数组中的偏移量。 如果这个buffer底层是由数组支持的,那么buffer的postion p对应于数组的index…...

【C++】C++ 引用详解 ⑤ ( 函数 “ 引用类型返回值 “ 当左值被赋值 )

文章目录 一、函数返回值不能是 " 局部变量 " 的引用或指针1、函数返回值常用用法2、分析函数 " 普通返回值 " 做左值的情况3、分析函数 " 引用返回值 " 做左值的情况 函数返回值 能作为 左值 , 是很重要的概念 , 这是实现 " 链式编程 &quo…...

Git,分布式版本控制工具

1.为常用指令配置别名&#xff08;可选&#xff09; 打开用户目录&#xff0c;创建.bashrc文件 &#xff08;touch ~/.bashrc&#xff09; 2.往其输入内容 #用于输出git提交日志 alias git-loggit log --prettyoneline --all --graph --abbrev-commit #用于输出当前目录所有文…...

LeetCode 面试题 02.02. 返回倒数第 k 个节点

文章目录 一、题目二、C# 题解 一、题目 实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点。返回该节点的值。 注意&#xff1a;本题相对原题稍作改动 点击此处跳转题目。 示例&#xff1a; 输入&#xff1a; 1->2->3->4->5 和 k 2 输出&#xff1a; 4 说…...

SpeedBI数据可视化工具:丰富图表,提高报表易读性

数据可视化工具一大作用就是能把复杂数据可视化、直观化&#xff0c;更容易看懂&#xff0c;也就更容易实现以数据驱动业务管理升级&#xff0c;因此一般的数据可视化工具都会提供大量图形化的数据可视化图表&#xff0c;以提高报表的易懂性&#xff0c;更好地服务企业运营决策…...

编写Dockerfile制作Web应用系统nginx镜像

文章目录 题目要求&#xff1a;一、创建文档&#xff0c;编写Dockerfile文件可以将harbor仓库去启动先起来 二、运行Dockerfile&#xff0c;构建nginx镜像三、推送导私有仓库&#xff0c;也就是我们的harbor仓库 题目要求&#xff1a; 编写Dockerfile制作Web应用系统nginx镜像…...

记录一次微服务连接Nacos异常-errorMsg: Illegal character in authority at index 7:

组件信息 Nacos 2.2.3 SpringCloud微服务 部署环境&#xff1a;centerOS 部署方式&#xff1a;k8s 前言 nacos开启鉴权&#xff0c;nacos地址通过变量方式传入服务中 PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.server-addr", "${NACO…...

【Java】反射 之 调用构造方法

调用构造方法 我们通常使用new操作符创建新的实例&#xff1a; Person p new Person();如果通过反射来创建新的实例&#xff0c;可以调用Class提供的newInstance()方法&#xff1a; Person p Person.class.newInstance();调用Class.newInstance()的局限是&#xff0c;它只…...

Hightopo 使用心得(6)- 3D场景环境配置(天空球,雾化,辉光,景深)

在前一篇文章《Hightopo 使用心得&#xff08;5&#xff09;- 动画的实现》中&#xff0c;我们将一个直升机模型放到了3D场景中。同时&#xff0c;还利用动画实现了让该直升机围绕山体巡逻。在这篇文章中&#xff0c;我们将对上一篇的场景进行一些环境上的丰富与美化。让场景更…...

【Python PEP 笔记】201 - 同步迭代 / zip() 函数的使用方法

原文地址&#xff1a;https://peps.python.org/pep-0201/ PDF 地址&#xff1a; 什么是同步迭代 同步迭代就是用 for 一次循环多个序列。 类似于这样的东西&#xff1a; arr1 [1, 2, 3, 4] arr2 [a, b, c, d] for a, b in arr1, arr2:print(a, b)使用 map 实现 for a, b …...

远程控制:用了向日葵控控A2后,我买了BliKVM v4

远程控制电脑的场景很多&#xff0c;比如把办公室电脑的文件发到家里电脑上&#xff0c;但是办公室电脑旁边没人。比如当生产力用的电脑一般都比较重&#xff0c;不可能随时带在身边&#xff0c;偶尔远程操作一下也是很有必要的。比如你的设备在工况恶劣的环境中&#xff0c;你…...

基于swing的火车站订票系统java jsp车票购票管理mysql源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于swing的火车站订票系统 系统有2权限&#xff1a;…...

MAVEN利器:一文带你了解IDEA中如何使用Maven

前言&#xff1a; 强大的构建工具——Maven。作为Java生态系统中的重要组成部分&#xff0c;Maven为开发人员提供了一种简单而高效的方式来构建、管理和发布Java项目。无论是小型项目还是大型企业级应用&#xff0c;Maven都能帮助开发人员轻松处理依赖管理、编译、测试和部署等…...

R语言15-R语言中的列的分裂与合并长宽数据转换

列的分裂与合并 列的分裂&#xff1a; 使用 separate() 函数将一个包含多个值的列分裂成多个列。 install.packages("tidyr") # 安装 tidyr 包&#xff08;如果尚未安装&#xff09; library(tidyr)data <- data %>%separate(col_name, into c("part1…...

使用Pytorch和OpenCV实现视频人脸替换

“DeepFaceLab”项目已经发布了很长时间了&#xff0c;作为研究的目的&#xff0c;本文将介绍他的原理&#xff0c;并使用Pytorch和OpenCV创建一个简化版本。 本文将分成3个部分&#xff0c;第一部分从两个视频中提取人脸并构建标准人脸数据集。第二部分使用数据集与神经网络一…...

【力扣】202. 快乐数 <哈希>

【力扣】202. 快乐数 编写一个算法来判断一个数 n 是不是快乐数。 【快乐数】 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。 如果这个过程…...

深度学习4. 循环神经网络 – Recurrent Neural Network | RNN

目录 循环神经网络 – Recurrent Neural Network | RNN 为什么需要 RNN &#xff1f;独特价值是什么&#xff1f; RNN 的基本原理 RNN 的优化算法 RNN 到 LSTM – 长短期记忆网络 从 LSTM 到 GRU RNN 的应用和使用场景 总结 百度百科维基百科 循环神经网络 – Recurre…...

自动驾驶感知传感器标定安装说明

1. 概述 本标定程序为整合现开发的高速车所有标定模块,可实现相机内参标定和激光、相机、前向毫米波 至车辆后轴中心标定,标定参数串联传递并提供可视化工具验证各个模块标定精度。整体标定流程如下,标定顺序为下图前标0-->1-->2-->3,相同编号标定顺序没有强制要求…...

基于JAYA算法优化的BP神经网络(预测应用) - 附代码

基于JAYA算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于JAYA算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.JAYA优化BP神经网络2.1 BP神经网络参数设置2.2 JAYA算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...