C/C++内存管理

news/发布时间2024/5/15 20:55:42

一. 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);
}
   选择题:选项: A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)globalVar在哪里?____   staticGlobalVar在哪里?____   staticVar在哪里?____  localVar在哪里?____    num1 在哪里?____char2在哪里?____       *char2在哪里?___pChar3在哪里?____      *pChar3在哪里?____ptr1在哪里?____        *ptr1在哪里?____

解答

  选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

  globalVar在哪里?__C__  staticGlobalVar在哪里?__C__

  staticVar在哪里?__C__  localVar在哪里?__A__

  num1 在哪里?__A__

  分析:

  globalVar全局变量在数据段 staticGlobalVar静态全局变量在静态区

  staticVar静态局部变量在静态区  localVar局部变量在栈区

  num1局部变量在栈区

  

  char2在哪里?__A__  *char2在哪里?__A__

  pChar3在哪里?__A__   *pChar3在哪里?__D__

  ptr1在哪里?__A__    *ptr1在哪里?__B__

  分析:

  char2局部变量在栈区  

  char2是一个数组,把后面常量串拷贝过来到数组中,数组在栈上,所以*char2在栈上

  pChar3局部变量在栈区   *pChar3得到的是字符串常量字符在代码段

  ptr1局部变量在栈区     *ptr1得到的是动态申请空间的数据在堆区

【说明】:

1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
    创建共享共享内存,做进程间通信。

3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
4. 数据段--存储全局数据和静态数据。
5. 代码段--可执行的代码/只读常量。

二. C语言中动态内存管理方式:malloc/calloc/realloc/free

void Test()
{   //mallocint* p1 = (int*)malloc(sizeof(int));free(p1);//callocint* p2 = (int*)calloc(4, sizeof(int));free(p2);//reallocint* p3 = (int*)realloc(p2, sizeof(int) * 10);free(p3);
}

【面试题】: malloc/calloc/realloc的区别?

1.malloc函数

函数原型:void* malloc(size_t n);
参数释义:n:申请空间大小

如果函数执行成功,malloc函数返回火的内存空间的首地址;

如果函数执行失败,则返回值为NULL。

由于malloc函数值的类型为void型指针,因此,可以将其值类型转化后赋给任意类型指针,可以通过操作该类型指针来操作从堆上获得的内存空间。
注意:通过malloc函数分配得到的内存空间是未初始化的,必须使用memset函数来初始化。

2.calloc函数

函数原型:void* calloc(int n,size_t size);
参数释义:n:申请空间的个数size:单个类型的大小

calloc函数的返回值类型为void指针类型。

如果函数执行成功,函数从堆上获得n*size的字节空间,并返回该空间的首地址;

如果函数执行失败,返回值为NULL。
calloc函数与malloc函数不同之处在于:calloc函数得到的内存空间是进过初始化的,其内容全为0

3.realloc函数

函数原型:
void* realloc(void* p,size_t n);
参数释义:n:空间的大小p:堆上已经存在的空间的地址

realloc函数将指针p指向的内存空间的大小改为n字节,通常用来扩容,当扩大一个内存空间时,若该空间后边的空间可以满足要求则会在原内存后开辟空间,若后边的空间不满足要求,则会在另一个地方开辟空间,并将原内存中的数据拷贝过来,并释放原内存空间,返回新空间的首地址
注意:realloc函数分配的空间是未初始化的

三. C++内存管理方式 

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

3.1 new/delete操作内置类型

void Test()
{//使用new不需要计算空间大小// 动态申请一个int类型的空间int* ptr4 = new int;// 动态申请一个int类型的空间并初始化为10int* ptr5 = new int(10);// 动态申请0个int类型的空间int* ptr6 = new int[10];// 动态申请10个int类型的空间并初始化int* ptr7 = new int[10]{ 10,9,8,7,6,5 };delete ptr4;delete ptr5;delete[] ptr6;delete[] ptr7;
}

注意:

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

3.2 new和delete操作自定义类型

new/delete 和 malloc/free最大区别是 new/delete对于自定义类型除了开空间还会调用构造函数和析构函数

class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};
int main()
{A* p2 = new A(1);delete p2;return 0;
}

四. operator new与operator delete函数(重要点进行讲解) 

4.1 operator new与operator delete函数(重点)

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

【注意】:operator new与operator delete不是运算符重载,是全局函数


4.2  operator new函数

operator new的底层实现是利用malloc进行的,由于malloc在申请空间失败时会返回NULL,在C++中遇到这种情况通常会采取抛异常,所以operator new函数封装了malloc,当申请空间失败时,如果应对措施用户设置了,则继续申请,否则抛异常

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}
return (p);
}

4.3 operator delete函数

operator delete的底层实现是通过free释放空间的

void operator delete(void *pUserData)
{_CrtMemBlockHeader * pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK);  /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg( pUserData, pHead->nBlockUse );__FINALLY_munlock(_HEAP_LOCK);  /* release other threads */__END_TRY_FINALLYreturn;
}
/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

4.4 operator new与operator delete函数的使用

int main()
{//operator new 与 operator delete函数的使用与malloc/free的使用类似,但是平常也不会用到这两个函数A* p1 = (A*)operator new(sizeof(A));operator delete(p1);A* p2 = (A*)malloc(sizeof(A));free(p2);return 0;
}

4.5  operator new与operator delete函数的意义(了解)

operator new与operator delete函数其实不单单是对malloc/free的封装:

1. 当显示定义出析构函数

如图所示,该大小为申请空间的大小,但是我们申请了10个A类型的的大小,也就是40个字节,但是编译器却申请了44个字节,为什么会多出来4个字节呢?

【解释】:多出来的4个字节是用来存储开辟元素的个数的

因为当delete[ ] p1时编译器不知道应该调用多少次析构函数,编译器会在需要的空间前多申请4个空间用来保存元素个数,最后返回的是实际操作的空间地址,这4个字节就是留给delete[ ]使用的,而这4个字节的开辟是通过operator new实现的

​​​​​​​

二:当没有定义析构函数

当没有显示定义出析构函数,编译器可能认为调用默认析构函数没有什么实际作用,就不会多开辟那四个字节(不同编译器处理方式不同,若编译器依然要调用多次析构函数,则还会多定义空间)

【问题】:为什么new/new[ ]要与delete/delete[ ]对应使用?不对应使用会有什么问题?

情况1:new内置类型

情况2:new无显示定义析构函数的自定义类型

情况3:new显示定义析构函数的自定义类型

情况1与情况2能运行正常的原因是:

new的类型没有显示定义的析构函数,而编译器在new[ ]时也没有开辟多余的空间

情况3运行崩溃的原因是:

new的类型有显示定义的析构函数,而编译器在new[ ]时开辟多余的空间用于保存元素个数,当delete时,会释放p2所指空间,但是该空间前多开辟的空间没有释放,属于在开辟内存中间释放内存,所以会报错

五. new和delete的实现原理 

5.1 内置类型

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

5.2 自定义类型

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

六. 常见面试题 

6.1malloc/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在释放空间前会调用析构函数完成空间中资源的清理

七.内存泄漏 

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

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

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

7.2内存泄漏分类(了解)

C/C++程序中一般我们关心两种方面的内存泄漏:
堆内存泄漏(Heap leak)
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。
系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

7.3如何避免内存泄漏

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

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

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

相关文章

HTML总结

1.网页 1.1 什么是网页 网站是指在因特网上根据一定的规则&#xff0c;使用 HTML等制作的用于展示特定内容相关的网页集合。 网页是网站中的一页&#xff0c;通常是 HTML格式的文件&#xff0c;它要通过浏览器来阅读。 网页是构成网站的基本元素&#xff0c;它通常由图片、…

git上传报错:Object too large, rejecting the pack

在gerrit设置了最大不能上传超过600M的文件&#xff0c;今天开发遇到推送问题&#xff1a; 结果到本地怎么也找不到大文件。 后来只能按commit排查&#xff0c;用如下命令排查到了&#xff1a; 解决方法,将大文件去掉&#xff1a;&#xff08;commitid为大文件所在commit&…

网络原理 - HTTP/HTTPS(4)

HTTP响应详解 认识"状态码"(status code) 状态码表示访问一个页面的结果.(是访问成功,还是失败,还是其它的一些情况...).(响应结果如何) 学习状态码 -> 为了调试问题. 写服务器时,按照状态码的含义正确使用. 200 OK 这是最常见的状态码,表示访问成功. 抓包抓…

Android 13.0 SystemUI下拉状态栏定制二 锁屏页面横竖屏通知栏都居中功能实现

1.前言 在13.0的系统rom定制化开发中,在关于systemui的锁屏页面功能定制中,由于在平板横屏通知栏功能中,通知栏总是显示在右边,并且是在右边居中显示的, 由于需要和竖屏显示一样,所以就需要用到在时钟下面显示通知栏,然后同样需要居中显示通知栏,所以就来分析下相关的…

npm常用命令

转载于&#xff1a;npm常用指令_npm指令-CSDN博客

Opencv简单图像操作

Opencv 一、读取图片 1.imshow Mat imread(const string& filename, intflags1 );flags: enum { /* 8bit, color or not */CV_LOAD_IMAGE_UNCHANGED -1, /* 8bit, gray */CV_LOAD_IMAGE_GRAYSCALE 0, /* ?, color */CV_LOAD_IMAGE_COLOR 1, /* any depth, ? */…

ES6 ~ ES11 学习笔记

课程地址 ES6 let let 不能重复声明变量&#xff08;var 可以&#xff09; let a; let b, c, d; let e 100; let f 521, g "atguigu", h [];let 具有块级作用域&#xff0c;内层变量外层无法访问 let 不存在变量提升&#xff08;运行前收集变量和函数&#…

Latex 公式 合并行与列

问题 latex 编辑公式时需要合并行与列 实例 使用 \multirow 和 \multicolumn % \footnotesize \small \begin{equation}\Delta l_p \left\{ \setlength\arraycolsep{2pt} \begin{array}{lll} A , & F, & \multirow[t]{2}{1cm}{$ \mathrm{for~} k 3,6,7, $} \…

Kotlin基本语法1到函数

1.range表达式 fun main() {var age 12.3if (age in 0.0..3.3){println("婴幼儿")}else if (age in 4.0..12.2){println("少儿")}else{println("未知")}/*** in 后面还可以接 list set 都可以*/if (age !in 0.0..3.3){println("婴幼儿&quo…

Unity UGUI的DrawCall优化

Unity UGUI是一种强大的用户界面设计工具&#xff0c;它可以帮助开发者快速创建各种界面元素&#xff0c;从按钮和文本到滑块和面板等。然而&#xff0c;在使用UGUI时&#xff0c;一个常见的性能瓶颈就是DrawCall过多导致的性能下降。在本文中&#xff0c;我们将深入探讨UGUI的…

智慧公厕:让智慧城市的公共厕所焕发“智慧活力”

智慧城市的建设已经进入了一个新的阶段&#xff0c;不仅仅是智慧交通、智慧环保&#xff0c;如今甚至连公厕都开始迎来智慧化时代。智慧公厕作为智慧城市的神经末梢&#xff0c;正在通过信息化、数字化和智慧化的方式&#xff0c;实现全方位的精细化管理。本文以智慧公厕源头专…

【机器学习笔记】12 聚类

无监督学习概述 监督学习 在一个典型的监督学习中&#xff0c;训练集有标签&#x1d466; &#xff0c;我们的目标是找到能够区分正样本和负样本的决策边界&#xff0c;需要据此拟合一个假设函数。无监督学习 与此不同的是&#xff0c;在无监督学习中&#xff0c;我们的数据没…

Vue源码系列讲解——生命周期篇【一】(综述)

1. 前言 在Vue中&#xff0c;每个Vue实例从被创建出来到最终被销毁都会经历一个过程&#xff0c;就像人一样&#xff0c;从出生到死亡。在这一过程里会发生许许多多的事&#xff0c;例如设置数据监听&#xff0c;编译模板&#xff0c;组件挂载等。在Vue中&#xff0c;把Vue实例…

ubuntu22.04@laptop OpenCV Get Started: 007_color_spaces

ubuntu22.04laptop OpenCV Get Started: 007_color_spaces 1. 源由2. 颜色空间2.1 RGB颜色空间2.2 LAB颜色空间2.3 YCrCb颜色空间2.4 HSV颜色空间 3 代码工程结构3.1 C应用Demo3.2 Python应用Demo 4. 重点分析4.1 interactive_color_detect4.2 interactive_color_segment4.3 da…

判断一个dll/exe是32位还是64位

通过记事本判断&#xff08;可判断C或者C#&#xff09; 64位、将dll用记事本打开&#xff0c;可以看到一堆乱码&#xff0c;但是找到乱码行的第一个PE&#xff0c;如果后面是d?则为64位 32位、将dll用记事本打开&#xff0c;可以看到一堆乱码&#xff0c;但是找到乱码行的第…

基础antdesign的业务型 短时间控件封装(复制即可使用)

{/* startFieldName 开始时间标识 endFieldName 结束时间标识 label 同form lable rules 是否开启规则校验 默认开启 detailData 详情数据&#xff0c;用于编辑回显 dateRange 限制结束时间的范围 例如&#xff1a;开始时间选择了 2024-02-05 &#xff0c;加上 dateRange3 后 只…

unity学习(31)——跳转到角色选择界面(打勾?手滑挂错脚本)

There are 2 audio listeners in the scene. Please ensure there is always exactly one audio listener in the scene. 是因为后来创建了一个camera&#xff0c;因为camera中自带一个组件Audio Listener。所以有两个camera就有两个audio listener导致报错。 一个简单的解决…

通过Linux终端搭建基于HTTP隧道的文件传输系统

嘿&#xff0c;Linux小侠们&#xff0c;准备好挑战一项酷炫的任务了吗&#xff1f;今天我们要一起通过Linux终端搭建一个基于HTTP隧道的文件传输系统&#xff0c;让我们的文件在网络的海洋中畅游无阻&#xff01; 在开始之前&#xff0c;让我们先来想象一下这个场景&#xff1…

Flink/flinksql 语法 窗口与join 一文全 相关概念api汇总总结,底层process算子总结,与数据延迟处理,超时场景解决方案

Flink 窗口概念与join汇总总结 1 SQL语法中窗口语法相关&#xff08;仅仅是flinksql中 窗口的语法&#xff09;1.1 sql窗口1.2 window topN 2 java/SQL join语法与介绍2.1 有界join2.1.1 Window Join2.1.2 Interval Join2.1.3 Temporary Join2.1.4 LoopUp Join2.2 无界join2.2.…

javascript中的行为委托设计模式【详解】

本人编程小白一枚&#xff0c;希望多多包涵~ 如果阅读有疑问的话&#xff0c;欢迎评论或私信&#xff01;&#xff01; 本人会很热心的阐述自己的想法&#xff01;谢谢&#xff01;&#xff01;&#xff01; 文章目录 深入探讨 JavaScript 行为委托设计模式什么是行为委托&…
推荐文章