C++:C/C++的内存管理

目录

C/C++内存分布

C语言中动态内存管理方式

C++内存管理方式

new/delete操作内置类型

new/delete操作自定义类型

operator new与operator delete函数

new和delete的实现原理

定位new表达式

常见问题

malloc/free和new/delete的区别

内存泄漏


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

1. 选择题:
选项: A.栈 B.堆 C.数据段 D.代码段
globalVar在哪里?__C__ staticGlobalVar在哪里?__C__
staticVar在哪里?__C__ localVar在哪里?__A__
num1 在哪里?__A__
char2在哪里?__A__ *char2在哪里?__A__
pChar3在哪里?__A__ *pChar3在哪里?__D__
ptr1在哪里?__A__ *ptr1在哪里?__B__
 
2. 填空题:
sizeof(num1) = __40__;
sizeof(char2) = __5__; strlen(char2) = __4__;
sizeof(pChar3) = __4/8__; strlen(pChar3) = __4__;
sizeof(ptr1) = __4/8__;

(从代码段到内存空间,是从低地址到高地址)

内存分配通常发生在以下几个区域:

  • 栈区(Stack):非静态局部变量、函数参数、返回值等都是存储在栈上,函数结束时,这些存储单元都会被释放。栈内存由编译器自动管理,按照后进先出(LIFO)的原则操作。栈是向下生长的。
  • 堆区(Heap):用于程序运行时的动态内存分配,由程序员手动分配和释放。使用malloc、calloc、realloc和free(C语言)或者new和delete(C++)进行管理。
  • 全局/静态存储区(Global/Static Storage):全局变量和静态变量存储在这里,程序结束后系统自动释放。
  • 常量存储区(Constant Storage):存储字符串字面量和常量,它们在程序运行时不可修改。
  • 代码段(Code Segment):存储可执行代码和只读数据,程序运行时不可修改。
  • 内存映射段(Memory Mapped Segment):是一种高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存,做进程间通信。

C语言中动态内存管理方式

C语言主要是用malloc/calloc/realloc/free来管理动态内存的。

malloc:

  1. 原型:void *malloc(size_t size)。
  2. 在堆上分配一块至少为size字节的内存区域,并返回一个指向这块内存的指针。
  3. 如果内存分配成功,返回的指针指向分配的内存;若失败,返回NULL。

calloc:

  1. 原型:void *calloc(size_t num, size_t size)。
  2. 在堆上分配一块总大小为num * size字节的内存区域,并将这块内存区域初始化为0。
  3. 该函数首先使用malloc分配内存,然后初始化内存。返回值同malloc。

realloc:

  1. 原型:void *realloc(void *ptr, size_t new_size)。
  2. 更改之前分配的内存大小为new_size字节。
  3. 若ptr为NULL,realloc就如同malloc一样,分配一块新内存,若new_size为0,realloc就如同free一样,释放申请的内存。
  4. 成功时返回重新分配内存的指针,失败时返回NULL。重新分配的内存可能会移动,因此返回的指针可能与一开始的ptr不同。

free:

  1. 原型:void free(void *ptr)。
  2. 释放之前用malloc、calloc或者realloc分配的内存。
  3. 使用完free后,记得将ptr置为NULL,可以避免野指针的问题。
void Test()
{int* p1 = (int*)malloc(sizeof(int));free(p1);int* p2 = (int*)calloc(8, sizeof(int));free(p2);int* p3 = (int*)realloc(p2, sizeof(int) * 20);free(p3);
}

C++内存管理方式

C语言内存管理方式在C++依然适用,但有些地方使用起来比较麻烦,所以C++又提出了自己的内存管理方式。

通过new和delete操作符进行动态管理。

new/delete操作内置类型

void Test()
{// new一个int类型的空间int* ptr1 = new int;// new一个int类型的空间并初始化为10int* ptr2 = new int(10);// new10个int类型的空间int* ptr3 = new int[10];// new10个int类型的空间并初始化int* ptr4 = new int[10]{ 8,7,6,5,4,3 }; //跟数组的初始化很像,大括号有几个,初始化几个,其余为0。不过C++11才支持的语法delete ptr1;delete ptr2;delete[] ptr3;delete[] ptr4;
}

总结

  • 申请和释放单个元素的空间,使用new和delete操作符。
  • 申请和释放连续的空间,使用new[]和delete[],注意匹配起来使用。一定要匹配使用不然的话有可能会出大bug。

 对于内置类型,malloc和new除了用法上有所不同,在其他方面就没有什么区别。它们的区别要在自定义类型才能体现。

new/delete操作自定义类型

再申请自定义类型空间,new/delete和malloc/free的区别:

new会申请空间然后调用构造函数初始化,delete会先调用析构函数销毁资源然后再销毁空间。而malloc和free仅仅是申请空间和销毁空间。

class A
{
public:A(int a = 0): _a(a){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{A* aa1 = (A*)malloc(sizeof(A));A* aa2 = new A;free(aa1);delete aa2;cout << endl;A* aa3 = new A[10];delete[] aa3;return 0;
}

 new和malloc还有一个明显的区别:它们再空间开辟失败的处理情况不同。

malloc开辟失败会返回空指针,但是new开辟失败会抛异常,这个异常我们是要捕获的。

operator new与operator delete函数

newdelete是用户进行动态内存申请和释放的操作符operator newoperator delete系统提供的全局函数

new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

我们利用反汇编来验证下。

operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果该应对措施用户设置了,则继续申请,否则抛异常operator new本质是封装了malloc。operator delete本质是封装了free。

具体使用:

int main()
{Stack* ps1 = (Stack*)operator new(sizeof(Stack));operator delete(ps1);Stack* ps2 = (Stack*)malloc(sizeof(Stack));assert(ps2);free(ps2);
}

这两个函数与malloc/free功能一样,也不会去调用构造函数初始化和析构函数销毁资源,但是它们还是有所区别。operator new不需要检查开辟空间的合法性,因为开辟失败了就抛异常。

opertor new和operator的类专属重载

有时候我们需要反复向堆申请释放空间,就是就有了内存池,找内存池申请释放空间,效率会更高,这两个函数就是在new调用operator new的时候走内存池的机制从而效率更高。

new和delete的实现原理

  • new的底层原理:调用operator new + 构造函数。
  • delete的底层原理:调用析构函数 + operator delete。

注意,后面两个函数的调用是有顺序的,不要误以为调换顺序也可以。

内置类型

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

自定义类型

  • 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来释放空间

定位new表达式

定位new表达式的作用主要已分配的原始内存空间中调用构造函数初始化一个对象

  • 使用格式
new (place_address) type或者new (place_address) type(initializer-list)

注意,place_address必须是一个指针,initializer-list是类型的初始化列表。

使用场景:

定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。

class Test
{
public:Test(int date = 2): _data(date){cout << "Test():" << this << endl;}~Test(){cout << "~Test():" << this << endl;}
private:int _data;
};
int main()
{// pt1现在指向的只不过是与Test对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行Test* pt1 = (Test*)malloc(sizeof(Test));//new (place_address) typenew(pt1)Test; // 注意:如果Test类的构造函数有参数时,此处需要传参pt1->~Test();free(pt1);//new(place_address) type(initializer - list)Test* pt2 = (Test*)malloc(sizeof(Test));new(pt2)Test(10);pt2->~Test();free(pt2);
}

常见问题

  • 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在释放空间前会调用析构函数完成空间中资源的清理(底层区别)

  • 内存泄漏

什么是内存泄漏

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

内存泄漏的危害

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

内存泄漏的分类

1. 堆内存泄漏(Heap leak)

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

2.系统资源泄漏

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

检侧内存泄漏

1.Windows下使用第三方工具:VLD工具说明

2.Linux下检测:linux下几款内存泄漏检测工具

3其他方法:内存泄漏工具比较

如何避免内存泄漏

1.工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。ps:这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智能指针来管理才有保证。
2.采用RAII思想或者智能指针来管理资源。
3.有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
4.出问题了使用内存泄漏工具检测。

所以总的来说,解决方案分两种:

1.事前预防:如智能指针等。

2.事后查错:如泄漏检测工具等。


拜拜,下期再见😏

摸鱼ing😴✨🎞

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/404548.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习】(基础篇七) —— 神经网络

神经网络 神经网络是一种模仿人脑神经元结构的计算模型&#xff0c;用于处理复杂的数据模式识别和预测问题。它由大量简单的处理单元&#xff08;称为“神经元”或“节点”&#xff09;组成&#xff0c;这些单元通过连接权重相互连接。神经网络可以学习从输入数据到输出结果之…

GitLab Merge Request流水线

GitLab Merge Request 流程文档 为了提升代码质量&#xff0c;让开发人员参与代码review&#xff0c;现在输出Merge Request的流程文档&#xff1a; 1.项目创建各自开发者的分支&#xff0c;命名规则是dev_名字首字母&#xff0c;比如我是dev_cwq.然后把本地分支推到远端orgin…

2024.8.19 学习记录 —— 作业

一、TCP机械臂测试 #include <myhead.h>#define SER_PORT 8888 // 与服务器保持一致 #define SER_IP "192.168.0.114" // 服务器ip地址int main(int argc, const char *argv[]) {// 创建文件描述符打开键盘文件int fd open("/dev/input/event1…

【数学建模】趣味数模问题——舰艇追击问题

问题描述 某缉私舰位于走私船以东 d 10 km&#xff0c;走私船以匀速 u 8 km/h 向北沿直线行驶。缉私舰立即以速度 v 12 km/h 追赶。缉私舰使用雷达进行跟踪&#xff0c;保持瞬时速度方向始终指向走私船。求解缉私舰的追逐路线和追上走私船所需的时间。 方法 理论求解&…

NIO中的异步—ChannelFuture、CloseFuture以及异步提升在NIO中的应用

ChannelFuture 客户端调用connect后返回值为ChannelFuture对象&#xff0c;我们可以利用ChannelFuture中的channel()方法获取到Channel对象。 由于上述代为为客户端实现&#xff0c;若想启动客户端实现连接操作&#xff0c;必须编写服务端代码&#xff0c;实现如下&#xff1a;…

python中的randint如何使用

python中的randint用来生成随机数&#xff0c;在使用randint之前&#xff0c;需要调用random库。random.randint()是随机生成指定范围内的整数&#xff0c;其有两个参数&#xff0c;一个是范围上限&#xff0c;一个是范围下限。 具体用法如下&#xff1a; import random print…

USB3.2 摘录(四)

系列文章目录 USB3.2 摘录&#xff08;一&#xff09; USB3.2 摘录&#xff08;二&#xff09; USB3.2 摘录&#xff08;三&#xff09; USB3.2 摘录&#xff08;四&#xff09; 文章目录 系列文章目录8 协议层&#xff08;Protocol Layer&#xff09;8.8 三个参数地址信息&…

苍穹外卖项目DAY07

苍穹外卖项目Day07 1、缓存菜品 1.1、问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 1.2、实现思路 通过Redis来缓存菜品的数据&#xff0c;减少数据库查询操作 缓存逻辑分析&#xff…

未来视界,触手可及:揭秘数字透明屏的奇幻之旅

在这个日新月异的科技时代&#xff0c;每一项创新都如同星辰般璀璨&#xff0c;引领着我们向更加智能、更加梦幻的未来迈进。今天&#xff0c;就让我们一起揭开一项颠覆传统视觉体验的前沿科技——数字透明屏的神秘面纱&#xff0c;探索它如何将未来视界&#xff0c;化为触手可…

IDEA:如何在idea中设置自动导包

这里使用的是idea2020版本,但是不同版本操作不会有较大的差别. 在Editer中展开General之后,选中Auto Import,最后勾选中Add unambiguous imports on the fly.

微信视频号评论如何快速采集?三种高效实用的方法

本文将深入探讨如何高效地采集微信视频号评论&#xff0c;通过揭秘三种实用方法&#xff0c;助您迅速掌握这一营销利器。从自动化工具到智能策略&#xff0c;每一步都旨在提升您的市场分析效率&#xff0c;让您在竞争激烈的社交媒体领域中脱颖而出。 一、引言&#xff1a;为何…

探索顶级PDF水印API:PDFBlocks(2024年更新)

引言 在一个敏感信息常常面临风险的时代&#xff0c;能够轻松高效地保护文档的能力至关重要。PDF水印已成为企业和个人寻求保护其知识产权、确保文件保密性的基本工具。 PDFBlocks 文字水印 API是什么&#xff1f; PDFBlocks API 提供了一个强大的解决方案&#xff0c;用于在…

day06——前后端交互

一、计算属性 计算属性就是基于现有的数据推算出来的新属性&#xff0c;只要依赖的数据变化&#xff0c;新属性就会自动更新&#xff0c;而且计算属性多次调用的情况下只会计算一次&#xff0c;效率非常高 简化写法 const app new Vue({ el: #app, data: {}, methods: {//跟da…

记录一次生产jvm问题的排查

记录一次生产问题的排查 第一天晚上 现象 1、前援反馈页面有接口陆续出现请求超时 2、登录后台服务器top命令查看发现java进程发生高cpu占用情况 3、查看对应业务日志&#xff0c;报数据库连接等待超时-数据库连接池连接无空闲 对应处理 1、临时调大数据库连接池最大连接数限…

Chat App 项目之解析(三)

Chat App 项目介绍与解析&#xff08;一&#xff09;-CSDN博客文章浏览阅读76次。Chat App 是一个实时聊天应用程序&#xff0c;旨在为用户提供一个简单、直观的聊天平台。该应用程序不仅支持普通用户的注册和登录&#xff0c;还提供了管理员登录功能&#xff0c;以便管理员可以…

【三维重建汇总】NeRF和GS重建中,如何排除干扰物?(提升质量)

汇总最近NeRF与GS提升质量的论文 文章目录 前言一、NeRF On-the-go&#xff1a;利用不确定性落地真实世界&#xff08;CVPR24&#xff09;摘要1.DINOv2特征的不确定性预测2.NeRF中干扰物去除的不确定性3.优化4. Dilated Patch 扩大采样5.实验结果 二、Pixel-GS:像素感知的梯度密…

unity程序简易框架

1. 框架基本结构 2. 单例模式基类模块 2.1 BaseManager.cs using System.Collections; using System.Collections.Generic; using UnityEngine;public class BaseManager<T> where T:new() {private static T instance;public static T GetInstance(){if (instance == …

高防服务器配置要素

高防服务器配置通常包括硬件资源、网络资源、防护能力、弹性防护、清洗能力和业务支持等方面。下面将详细介绍高防服务器的配置要素&#xff0c;rak部落小编为您整理发布。 高防服务器是设计用来抵御各种网络攻击&#xff0c;特别是分布式拒绝服务(DDoS)攻击的服务器配置。这些…

伊朗通过 ChatGPT 试图影响美国大选, OpenAI 封禁多个账户|TodayAI

OpenAI 近日宣布&#xff0c;他们已经封禁了一系列与伊朗影响行动有关的 ChatGPT 账户&#xff0c;这些账户涉嫌利用该 AI 工具生成并传播与美国总统选举、以色列 – 哈马斯战争以及奥运会等相关的内容。 OpenAI 表示&#xff0c;这些账户与一个名为 “Storm-2035” 的秘密伊朗…

技术速递|Python in Visual Studio Code 2024年8月发布

排版&#xff1a;Alan Wang 我们很高兴地宣布 Visual Studio Code 的 Python 和 Jupyter 扩展将于 2024 年 8 月发布&#xff01; 此版本包括以下公告&#xff1a; 使用 python-environment-tools 改进了 Python 发现源代码中显示的内联变量值对 Python 的 VS Code Native RE…