C/C++内存管理(超详解)

目录

1.C/C++内存分布

2.C语言动态内存管理

2.1 malloc

2.2 free

2.3 calloc

2.4 realloc

3.C++动态内存管理

3.1new/delete操作内置类型

3.2new/delete操作自定义类型

3.3operator new与operator delete函数

3.4定位new表达式(placement-new)


1.C/C++内存分布

内存中是如何布局的?如下图,,一般内存主要分为:代码区、常量区、静态区(全局区)、堆区、栈区这几个区域。

内存分布说明:

  1. 内核空间: 放置操作系统相关的代码和数据。(用户不能直接进行操作 ------ 可以通过调用系统提供的 api 函数)一般是给操作系统预留的内存空间,系统会进行相应的保护。
  2. 栈又叫堆栈:非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  3. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
  4. 堆用于程序运行时动态内存分配,堆是向上增长的。
  5. 数据段:存储全局数据和静态数据。(如static修饰的变量或在函数外定义的全局变量)
  6. 代码段:可执行的代码/只读常量。(如常量字符串)

2.C语言动态内存管理

2.1 malloc

函数信息:

上代码:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//开辟10个整型的空间:
	
	int* p = (int*)void* p = malloc(10 * sizeof(int));

	//1.开辟失败
	if(p == NULL)
	{
		perror("main");
		return 0;
	}
	//2.开辟成功
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	//回收空间
	free(p);
	p = NULL;
	return 0;
}

小结:

  1. malloc向堆区申请一块连续的空间,开辟成功返回那块空间的地址,开辟失败返回NULL
  2. 因为malloc函数的返回值是void*类型,在用指针变量接收时,必须强制类型转换为对应类型
  3. 如果malloc开辟失败,可能会对空指针进行非法解引用操作,malloc开辟的空间一定要检查
  4. 使用完空间,要主动回收。因为在回收空间后,那块空间的使用权交给了操作系统,为了避免非法访问,通常会主动将指向那块空间的指针置为NULL

2.2 free

函数信息:

上代码:

#include<stdio.h>
int main()
{
	int a = 1;
	int* b = &a;
	free(b);//回收空间
	b = NULL;
	return 0;
}

小结:

  1. 如果参数ptr指向的空间不是动态开辟的,那么free函数的行为是标准未定义的
  2. 如果参数ptr是NULL指针,则视为无效代码,因为不能对一个不存在的地址释放空间,这样没有意义!
  3. 关于空间回收:可以是主函数结束后,所有开辟的栈帧会被还给操作系统,也可以是主动把不用的空间主动回收掉。

2.3 calloc

函数信息:

上代码:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//malloc与calloc进行对比
	//malloc
	int* p = (int*)malloc(40);
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		printf("%d\n", *(p + i));
	}
	free(p);
	p = NULL;
	
	//calloc
	int* q = (int*)calloc(10, sizeof(int));
	for(i = 0; i < 10; i++)
	{
		printf("%d\n", *(q + i));
	}
	free(q);
	q = NULL;
	return 0;
}

小结:

  1. 相比于malloc来说,calloc与malloc唯一的不同就是calloc会进行初始化,并且要指定开辟空间的单个大小及个数。

2.4 realloc

函数信息:

上代码:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//开辟10个整形大小
	int* p = (int*)calloc(10, sizeof(int));
	if(p == NULL)
	{
		perror("main");
		return 0;
	}
	//如果空间不够,还需要10个整型空间,使用realloc调整空间	
	int* pnew = (int*)realloc(p, 20 * sizeof(int));
	if(pnew != NULL)
	{
		p = pnew;	
	}
	//回收空间
	free(p);
	p = NULL;
	return 0;
}

小结:

realloc开辟空间原理:

  1. realloc在调整空间时,发现后面有足够的空间能调整,则直接紧挨着原空间之后进行调整,并且返回原空间的地址;
  2. realloc在调整空间时,发现后面没有足够的空间能调整,则会找一块能容纳原空间和需要增加的增加空间的新空间,并把原空间的内容全部拷贝到新空间,同时把原空间主动释放掉,最后在返回新空间的地址。

3.C++动态内存管理

3.1new/delete操作内置类型

关于new和delete我们通过代码来介绍:

int main()
{
	// 动态申请一个int类型的空间
	int* ptr4 = new int;
	// 动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);
	// 动态申请10个int类型的空间
	int* ptr6 = new int[10];
	delete ptr4;
	delete ptr5;
	delete[] ptr6;
	return 0;
}
讲解
申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,将new[]和delete[]匹配起来使用。如果要进行初始化的话,对于内置类型单个变量使用(初始值),如果是数组则在[]后加上{初始值1,初始值2,初始值3......};

3.2new/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* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A(1);
	free(p1);
	delete p2;//这里将delete和C语言中的free混用对于自定义类型来说看不出区别
	          //但对于内置类型有很大概率会出bug,不是好的代码习惯!

	int* p3 = (int*)malloc(sizeof(int)); 
	int* p4 = new int;
	free(p3);
	delete p4;//这样才是正确回收空间操作,malloc匹配free,new匹配delete
	A* p5 = (A*)malloc(sizeof(A) * 10);
	A* p6 = new A[10];//这里可以对A数组进行初始化,可以直接给值,也可以用匿名对象构造初始化
	//A* p7 = new A[10]{ 1,2,3,4,5,6,7,8,9,10 };
	//A* p8 = new A[10]{ A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),A(10) };
	free(p5);
	delete[] p6;
	return 0;
}

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会,这是C++动态操作与C语言动态操作的区别之一,为什么会调用析构和构造函数,请继续往下看:

先来介绍一下operator new和operator delete:

3.3operator newoperator delete函数

先弄清几个点:

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

纸上得来终觉浅,我们扒开两个函数的底层实现看看:

/*
operator new:该函数实际通过malloc来申请空间,当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);
}
/*
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_FINALLY
return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

小结:

  1. operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。如何查看异常呢,使用try+catch来捕捉:
    #include"iostream"
    using namespace std;
    class A
    {
    public:
    	A(int year=2025,int month=1,int day=19)
    	{
    		cout << year << "-" << month << "-" << day << endl;
    		this->_year = year;
    		this->_month = month;
    		this->_day = day;
    	}
    	~A()
    	{
    		cout << "Destroy" << endl;
    		_year = _month = _day = 0;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	try
    	{
    		A* tmp = new A[10]{ A(2025,1,10),A(2025,1,11) };//先operator new->malloc再构造
    		delete[] tmp;//先析构再operator free->free
    	}//malloc失败返回空指针 new失败抛异常捕捉
    	catch (const exception& e)
    	{
    		cout << e.what() << endl;
    	}
    
    	return 0;
    }
  2. operator delete 最终是通过free来释放空间的
  3. new操作符首先调用operator new全局函数通过底层malloc开辟一块空间,再调用构造函数对自定义类型初始化。
  4. delete操作符首先调用析构函数销毁自定义类型占用的空间,再调用operator delete全局函数通过底层free释放空间。

3.4定位new表达式(placement-new)

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是 配合内存池使用 。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用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;
}
由于内容太过超前,作者暂且介绍这么多~
- - - - - - ——————————本文结束—————————— - - - - - -

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【React】静态组件动态组件

目录 静态组件动态组件创建一个构造函数(类)使用 class 实现组件**使用 function 实现类组件** 静态组件 函数组件是静态组件&#xff1a; 组件第一次渲染完毕后&#xff0c;无法基于内部的某些操作让组件更新「无法实现自更新」&#xff1b;但是&#xff0c;如果调用它的父组…

前端【3】--CSS布局,CSS实现横向布局,盒子模型

盒子分类 1、块级盒子 2、内联级盒子 3、内联块级盒子 4、弹性盒子 5、盒子内部分区 方法一&#xff1a;使用 float 普通盒子实现横向布局 方法二&#xff1a;使用 display: inline-block 内联块级元素实现横向布局 方法三&#xff1a;使用弹性盒子 flexbox&#xff0…

Python实现过年烟花效果及打包成可执行文件

博客&#xff1a;Python实现过年烟花效果及打包成可执行文件 在这篇博客中&#xff0c;我们将详细讲解如何使用Python和Pygame库实现一个过年烟花效果的程序&#xff0c;并介绍如何将Python脚本打包成Windows上可以直接执行的exe文件。我们将从代码的各个模块入手&#xff0c;…

新星杯-ESP32智能硬件开发--ESP32的I/O组成-系统中断矩阵

本博文内容导读&#x1f4d5;&#x1f389;&#x1f525; ESP32开发板的中断矩阵、功能描述与实现、相关API和示例程序进行介绍 ESP32中断矩阵将任一外部中断源单独分配到每个CPU的任一外部中断上&#xff0c;提供了强大的灵活性&#xff0c;能适应不同的应用需求。 ESP32中断主…

SpringBoot2 + Flowable(UI)

文章目录 引言I 技术栈软件架构基于 Vue.js 和 Element UI 的后台管理系统工程结构II 依赖rest,logic,conf 的依赖工作流flowable jar包flowable-ui所需jar包III 配置jdbc 配置 nullCatalogMeansCurrent = true引言 I 技术栈 软件架构 前端基于vue 、element-ui框架分模块设…

.Net 6.0 .Net7.0 .Net8.0 .Net9.0 使用 Serilog 按日志等级写入日志及 appsetting.json 配置方式实现

前言 最近使用最新版的Serilog记录日志时&#xff0c;发现以前有些关于Serilog的Nuget弃用了&#xff0c;最关键的是有些配置写法也改变&#xff0c;于是就整理了一下最新版的Serilog配置方式(appsetting.json)的使用 说明&#xff1a;我是用的.Net6&#xff0c;最新长期支持…

sprnigboot集成Memcached

安装Memcached 下载地址 32位系统 1.2.5版本&#xff1a;http://static.jyshare.com/download/memcached-1.2.5-win32-bin.zip 32位系统 1.2.6版本&#xff1a;http://static.jyshare.com/download/memcached-1.2.6-win32-bin.zip 32位系统 1.4.4版本&#xff1a;http://stati…

【数据分析】02- A/B 测试:玩转假设检验、t 检验与卡方检验

一、背景&#xff1a;当“审判”成为科学 1.1 虚拟场景——法庭审判 想象这样一个场景&#xff1a;有一天&#xff0c;你在王国里担任“首席审判官”。你面前站着一位嫌疑人&#xff0c;有人指控他说“偷了国王珍贵的金冠”。但究竟是他干的&#xff0c;还是他是被冤枉的&…

3dmax LOGO的符号、意义和历史,渲染100邀请码1a12

Autodesk 3ds Max 是一款 3D 建模、动画和渲染软件&#xff0c;由 Autodesk, Inc. 于 1996 年开发&#xff0c;其功能是能够创建复杂的数字场景和视觉效果&#xff0c;被专业建筑师、设计师和视频游戏创作者广泛使用&#xff0c;提供了七种语言的 Windows 版本&#xff0c;没有…

线段树优化dp,abc389F - Rated Range

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 F - Rated Range 二、解题报告 1、思路分析 考虑定义 f(i, j) 为 初始分…

青少年CTF练习平台 EasyMD5解题思路

题目 EasyMD5 PHP弱类型/弱等于的判断 翻译 上传之后网页提示&#xff1a;Not a PDF! angry!!! get out from my page 修改文件后缀为pdf 再次上传&#xff0c;答案出来了 s878926199a s155964671a 成功获取flag

Amazon MSK 开启 Public 访问 SASL 配置的方法

1. 开启 MSK Public 1.1 配置 MSK 参数 进入 MSK 控制台页面&#xff0c;点击左侧菜单 Cluster configuration。选择已有配置&#xff0c;或者创建新配置。在配置中添加参数 allow.everyone.if.no.acl.foundfalse修改集群配置&#xff0c;选择到新添加的配置。 1.2 开启 Pu…

SW - 钣金零件保存成DWG时,需要将折弯线去掉

文章目录 SW - 钣金零件保存成DWG时&#xff0c;需要将折弯线去掉概述笔记备注END SW - 钣金零件保存成DWG时&#xff0c;需要将折弯线去掉 概述 如果做需要弯折的切割件&#xff0c;最好做成钣金零件。 最近做了几个小钣金(将钣金展开&#xff0c;建立新草图&#xff0c;在2…

深度学习 Pytorch 基本优化思想与最小二乘法

在正式开始进行神经网络建模之前&#xff0c;我们还需要掌握pytorch中最核心的基础数学工具——autograd(自动微分)模块。虽然对于任何一个通用的深度学习框架都会提供许多自动优化的算法和现成的loss function&#xff0c;但如果想更深入理解神经网络&#xff0c;对深度学习的…

Ceph与RAID在存储中的协同工作过程

本文将结合架构图&#xff0c;详细讲解Ceph与RAID如何在存储环境中相互配合&#xff0c;共同提供高效且可靠的存储服务。 架构概述 从上图中可以看到&#xff0c;Ceph的架构主要分为四个层次&#xff1a; 客户端和服务接口层&#xff1a;这一层包括客户端访问存储应用的接口…

PyTest自学-认识PyTest

1 PyTest自学-认识PyTest 1.1 PyTest可以用来做什么&#xff1f; PyTest是一个自动化测试框架&#xff0c;支持单元测试和功能测试&#xff0c;有丰富的插件&#xff0c;如&#xff0c;pytest-selemium, pytest-html等。 1.2 安装pytest 使用pip install -U pytest。 1.3 py…

【MathType】mathtype在word中格式问题

【MathType】mathtype在word中格式问题 1. 问题解决方法效果 2.新的问题解决方法效果 1. 问题 mathtype在word中格式显示不全 解决方法 CtrlC&#xff1a;选中全部——>段落——>设置为单倍行距 效果 已经可以全部显示出来&#xff0c;但是还有新问题&#xff01;…

当设置dialog中有el-table时,并设置el-table区域的滚动,看到el-table中多了一条横线

问题&#xff1a;当设置dialog中有el-table时&#xff0c;并设置el-table区域的滚动&#xff0c;看到el-table中多了一条横线&#xff1b; 原因&#xff1a;el-table有一个before的伪元素作为表格的下边框下&#xff0c;初始的时候已设置&#xff0c;在滚动的时候并没有重新设置…

华为AI培训-NLP实验

中文分词、命名实体识别、语义词性标注、语句逻辑推理、文本摘要、机器翻译、文本情感分析、内容创作 1 实验介绍 1.1 实验背景 中文分词、命名实体识别、语义词性标注、语句逻辑推理是自然语言处理领域中的重要任务。中文分词是将连续的汉字序列切分成有意义的词语序列…

一文大白话讲清楚webpack基本使用——4——vue-loader的配置和使用

一文大白话讲清楚webpack基本使用——4——vue-loader的配置和使用 1. 建议按文章顺序从头看是看 第一篇&#xff1a;一文大白话讲清楚啥是个webpack第二篇&#xff1a;一文大白话讲清楚webpack基本使用——1——完成webpack的初步构建第三篇一文大白话讲清楚webpack基本使用…