c/c++内存管理

news2024/12/26 17:00:11

前言:

        开篇前就聊聊篮球,在众多球星中,我觉得杜兰特(KD)非常专注于篮球,他一直坚持他所热爱的事业。尽管有很多缺点,但是他对于篮球的态度是坚定不移,这是我非常钦佩的。当然库里,乔治,哈登,或者正处于风波的欧文等等都是很好的球员。

本人是一个非常喜欢篮球的博主,最近有忙于篮球的训练。经过身体的疲劳后会选择休息,空闲时更是不愿意动,就会寻找适当的借口,选择摆烂。但是当听到有人说:有的时候人应该给自己一点狠劲。听到时有一种莫名的对自己的愧疚感,而这句话对于我还是很受用的。当然,当你选择松懈或者摆烂这句话就送给自己。

这篇文章会主要讲内存管理,其实在学c语言的时候就已经有提到,就是每块内存中的不同区域做着不同事情,他们有着不同的任务,实现这不同的功能,但是他们的本质都是存储数据。这个就像篮球打全场一样,1号位-5号位有着不同的任务,比如1号位负责带球过半场,4号5号负责挡拆,当他们上场的时候都是一个整体而运作起来,他们的本质也是一样就为了得分。很多时候计算机的场景就是生活的缩影。

目录

前言:

C/C++内存分布        

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

C++内存管理方式

 new/delete操作内置类型

new和delete操作自定义类型

operator new与operator delete函数 

new和delete的实现原理

内置类型

自定义类型

定位new表达式(placement-new)

常见面试题

内存泄漏分类


C/C++内存分布        

为了更好的介绍C++的内存管理,我们先来复习一下进程地址空间中各个区段的分布吧!

栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。

内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存,做进程间通信。

用于程序运行时动态内存分配,堆是可以上增长的。 

数据段--存储全局数据和静态数据。 

代码段--可执行的代码/只读常量。

那么下面通过试题来深入理解:

代码和相关问题

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__

char2局部变量在栈区  

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

--会自动释放函数中数组的值,像是临时变量。静态数组在函数里声明是存放在中的

pChar3局部变量在栈区   

*pChar3得到的是字符串常量字符在代码段

ptr1局部变量在栈区     

*ptr1得到的是动态申请空间的数据在堆区

2. 填空题:
sizeof(num1) = __40__;
sizeof(char2) = __5__; strlen(char2) = __4__;
sizeof(pChar3) = __4/8__; strlen(pChar3) = __4__;
sizeof(ptr1) = __4/8__;

3. sizeof 和 strlen 区别?

1.含义上有不同,一个为运算符号、另一个为函数表达。

sizeof它是运算符号,而strlen则是函数,有不同的用法。

2、和初始化的关系有不同

sizeof可以用于做参数,与初始化有一定的关系,而strlen则只能用char做参数,而且有限制,不是直接可以做,且与初始化无关,这也是两者的区别所在。

3.计算的时间不一样,一个在编译时,另一个在运行时。

strlen和sizeof 的计算时间不一样,sizeof在编译时就能计算,而strlen只能在运算时才能计算,这是需要大家注意的一点。

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

void Test()
{
	int* p1 = (int*)malloc(sizeof(int));
	free(p1);

	// 1.malloc/calloc/realloc的区别是什么?

	int* p2 = (int*)calloc(4, sizeof (int));
	int* p3 = (int*)realloc(p2, sizeof(int)* 10);

	// 这里需要free(p2)吗?
    // 不需要,free(p3)的时候就将p2释放了。

	free(p3);
}

【面试题】 

malloc/calloc/realloc的相同点和区别? 

相同点:

1.都是从堆上申请空间
2.都需要对返回值判空
3.都需要用户free释放
4.返回值类型相同(void*)
5.都需要类型转化
6.底层实现上是一样的,都需要开辟多余的空间,用来维护申请的空间

区别:

1.函数名字不同和参数类型不同。
2.calloc会对申请空间初始化,并且初始化为0,而其他两个不会。
3.malloc申请的空间必须使用memset初始化
4.realloc是对已经存在的空间进行调整,当第一个参数传入NULL的时候和malloc一样

malloc的实现原理?

C++内存管理方式

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

 new/delete操作内置类型

void main()
{
	// 动态申请一个int类型的空间
	int* ptr1 = new int;
	*ptr1 = 5;
	cout << *ptr1 << endl << endl;

	// 动态申请一个int类型的空间并初始化为6	
	int* ptr2 = new int(6);
	cout << *ptr2 << endl << endl;


	// 动态申请5个int类型的空间
	int* ptr3 = new int[5];
	for (int i = 0; i < 5;i++)
	{
		ptr3[i] = i;
		cout << ptr3[i] <<' ';
	}

	delete ptr1;
	delete ptr2;
	delete ptr3;
}

注意:申请和释放单个元素的空间,使用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;
};

int main()
{
	// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数
	A* p1 = (A*)malloc(sizeof(A));
	free(p1);

	A* p2 = new A(1);
	delete p2;

	// 内置类型是几乎是一样的
	int* p3 = (int*)malloc(sizeof(int)); // C
	free(p3);
	int* p4 = new int;	
	delete p4;

	A* p5 = (A*)malloc(sizeof(A)* 10);
	free(p5);
	A* p6 = new A[10];
	delete[] p6;
	//直观来看在自定义类型来说,用new和delete是更加间接是

	return 0;
}

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

free不会。

operator new与operator delete函数 

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

代码:

void Test()
{
	while (1)
	{
		// new失败 抛异常 -- 不需要检查返回值
		char* p1 = new char[1024 * 1024 * 1024];
		//char* p1 = (char*)operator new(1024 * 1024 * 1024);
		cout << (void*)p1 << endl;
	}
}

int main()
{
	Test();

	return 0;
}

 反汇编

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

new和delete的实现原理

内置类型

如果申请的是内置类型的空间,new和malloc,delete和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表达式(placement-new)

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

使用格式:

new (place_address) type或者new (place_address) type(initializer-list) place_address必须是一个指针,initializer-list是类型的初始化列表 

使用场景:

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

class A

{

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

private:
 int _a;
};
// 定位new/replacement new

int main()
{
 // p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没
有执行

 A* p1 = (A*)malloc(sizeof(A));
 new(p1)A;  // 注意:如果A类的构造函数有参数时,此处需要传参

 p1->~A();
 free(p1);
 A* p2 = (A*)operator new(sizeof(A));
 new(p2)A(10);
 p2->~A();
 operator delete(p2);
  return 0;
}

常见面试题

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。

系统资源泄漏

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

如何检测内存泄漏

因此写代码时一定要小心,尤其是动态内存操作时,一定要记着释放。但有些情况下总是防不胜 防,简单的可以采用上述方式快速定位下。如果工程比较大,内存泄漏位置比较多,不太好查时 一般都是借助第三方内存泄漏检测工具处理的。 

在linux下内存泄漏检测:linux下几款内存泄漏检测工具

在windows下使用第三方工具:VLD工具说明

其他工具:内存泄漏工具比较

如何避免内存泄漏

1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。ps: 这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智 能指针来管理才有保证。

2. 采用RAII思想或者智能指针来管理资源。

3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。

4. 出问题了使用内存泄漏工具检测。ps:不过很多工具都不够靠谱,或者收费昂贵。

总结一下:

内存泄漏非常常见,解决方案分为两种:

1、事前预防型。如智能指针等。

2、事后查错型。如泄 漏检测工具。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据环境搭建 —— VMware Workstation 安装详细教程

大数据系列文章&#xff1a;&#x1f449; 目录 &#x1f448; 文章目录一、下载安装包1. 下载 VMware Workstation2. 小技巧二、安装软件1. 软件安装2. 虚拟环境搭建一、下载安装包 1. 下载 VMware Workstation ① 打开 VMware Workstation 官方下载网站 VMware Workstati…

【Linux】管理文件和目录的命令大全

目录 Linux 管理文件和目录的命令 1.命令表 2.细分 1.pwd命令 2.cd 命令 3.ls 命令 4.cat 命令 5.grep 命令 6.touch 命令 7.cp 命令 8.mv 命令 9.rm 命令 10.mkdir 命令 11.rmdir 命令 赠语&#xff1a;Even in darkness, it is possible to create light.即使在…

C++构造函数

构造函数详解 类的6个默认的成员函数: 类中如果什么都没有定义:---有六个默认的成员函数: 构造函数:主要完成对象的初始化工作析构函数:主要完成对象中资源的清理工作拷贝构造函数:拷贝一个新的对象赋值运算符重载: 让两个对象之间进行赋值引用的重载:普通和const类型--->…

【Vue】VueCLI 的使用和单文件组件(2)

首先作为一个工程来说&#xff0c; 一般我们的源代码都放在src目录下&#xff1a; 外面的代码我们先不去管它&#xff0c;后面在工程编写的时候再给大家仔细的介绍。‍‍ 这块大家主要知道我们的源代码 都在src里面&#xff0c;它的入口文件是一个man点js文件&#xff0c;‍‍…

【day21】每日一题——MP3光标位置

MP3光标位置_牛客题霸_牛客网 这题就是简单的根据它的规则把它的情况都列举出来即可&#xff08;当然&#xff0c;我第一次写一脸懵逼&#xff0c;所以你现在一脸懵逼没事&#xff0c;看完你就觉得简单了。看完还懵逼&#xff0c;你就多看几遍&#xff0c;然后自己去尝试一下&a…

C/C++,不废话的宏使用技巧

经典废话 下面的所有内容全是我在欣赏一串代码时发出的疑问&#xff0c;之前对宏的了解不多&#xff0c;导致在刚看到下面的这串代码的时候是“地铁 老人 手机”&#xff0c;具体代码如下&#xff0c;如果有对这里解读有问题的欢迎在评论区留言。 一、预定义宏 编译一个程…

在线就能制作活动邀请函,一键生成链接

今天小编教你如何在线制作一个活动邀请函&#xff0c;不需要下载软件&#xff0c;也不需要编程代码&#xff0c;只需使用乔拓云工具在线一键就能生成活动邀请函和邀请函链接&#xff0c;下面就跟着小编的教学开始学习如何在线制作活动邀请函&#xff01;第一步&#xff1a;打开…

[附源码]java毕业设计SSM归途中流浪动物收容与领养管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

OSPF高级配置——虚接口,NSSA

作者介绍&#xff1a; 作者&#xff1a;小刘在C站 每天分享课堂笔记&#xff0c;一起努力&#xff0c;共赴美好人生&#xff01; 夕阳下&#xff0c;是最美的绽放。 目录 一.ospf 虚链路 二.虚链路的目的 三.配置虚链路的规则及特点 四.虚链路的配置&#xff1a; nssa …

HTML小游戏6 —— 《高达战争》横版射击游戏(附完整源码)

&#x1f482; 网站推荐:【神级源码资源网】【摸鱼小游戏】&#x1f91f; 风趣幽默的前端学习课程&#xff1a;&#x1f449;28个案例趣学前端&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】&#x1f4ac; 免费且实用的计算机相关知…

奥密克戎 (Omicron) 知多少m?| MedCheExpress

这个冬天 Omicron 已迅速超越其他变种&#xff0c;成为主要的 SARS-CoV-2 毒株&#xff0c;尽管该变体在体内引起的病毒水平与其“竞争对手” Delta 相比更低&#xff0c;但威力不容小觑。 ■ 第五大变异关注病毒株&#xff0c;有何神奇之处&#xff1f; 2021 年 11 月 24 日&…

深度自定义mybatis

> 回顾mybatis的操作的核心步骤 > > 编写核心类SqlSessionFacotryBuild进行解析配置文件 > 深度分析解析SqlSessionFacotryBuild干的核心工作 > > 编写核心类SqlSessionFacotry > 深度分析解析SqlSessionFacotry干的核心工作 > 编写核心类SqlSession &…

【面试官让我十分钟实现一个链表?一个双向带头循环链表甩给面试官】

我们在面试中面试官一般都会让我们现场写代码&#xff0c;如果你面试的时候面试官让你十分钟写一个链表&#xff0c;你是不是懵逼了&#xff1f;十分钟写一个链表&#xff0c;怎么可能&#xff1f;事实上是有可能的&#xff0c;十分钟写出的链表也能震惊面试官。 我们学习单链…

《红楼梦》诗词大全

前言&#xff1a; 博主最近二读红楼&#xff0c;幼时只觉此书开篇便人物繁杂、莺莺燕燕似多混乱&#xff0c;开篇只看黛玉哭闹了几次&#xff0c;便弃书不读&#xff0c;只觉困惑&#xff0c;其何敢称六大奇书或四大名著&#xff1f; 今日书荒&#xff0c;偶然间再次拾起红楼…

3.2 网络协议

0 socket协议 访问 Internet 使用得最广泛的方法&#xff1b;所谓socket通常也称作"套接字"&#xff0c;用于描述IP地址和端口&#xff0c;是一个通信链的句柄&#xff1b;应用程序通常通过"套接字"向网络发出请求或者应答网络请求&#xff1b;Socket接口…

六六大顺 马蹄集

六六大顺 难度&#xff1a;白银 0时间限制&#xff1a;1秒 巴占用内存&#xff1a;64M 输入正整数N,输出N以内&#xff08;含N),6的倍数&#xff0c;并且包含6的数字&#xff0c;比如36等。 格式 输入格式&#xff1a;输入整型 输出格式&#xff1a;输出整型&#xff0c;空格分…

SI24R1国产低功耗2.4GHz收发一体射频遥控工控答题卡方案芯片替代NRF24L01+

目录SI24R1简介芯片特性硬件设计参考2.4GHz射频芯片选型参考应用领域SI24R1简介 Si24R1 2.4GHz收发一体芯片量产于2012年&#xff0c;由于其一致性稳定性高、低功耗、远距离、兼容替代NRF24L01&#xff0c;兼容NORDIC 2.4GHz协议等特点&#xff0c;一直广泛应用于各物联网场景…

牛客竞赛每日俩题 - 动态规划2

目录 经典DP - 走方格 走方格2.0 分割回文串 分割回文串 - 回文优化 经典DP - 走方格 不同路径的数目(一)_牛客题霸_牛客网 状态&#xff1a; 子状态&#xff1a;从(0,0)到达(1,0),(1,1),(2,1),...(m-1,n-1)的路径数 F(i,j): 从(0,0)到达F(i,j)的路径数 状态递推&#xff1a…

【23届秋招总结系列】一个普本23届小学弟的秋招总结,上岸金山云开发(云计算方向)

大家好&#xff0c;我是路飞~ 正值秋招收尾阶段&#xff0c;今天很荣幸请来了交流qun小分队里的一位23届本科上岸 金山云开发工程师-云计算方向的同学&#xff0c;给大家分享一下他在秋招过程中的总结和心得体会。 他的博客链接&#xff1a;团子的守护 一、秋招收获 2022.1…

计算机毕业设计SSM大学生创新创业项目活动管理平台【附源码数据库】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…