C/C++ ---- 内存管理

目录

C/C++内存分布

常见区域介绍

经典习题(读代码回答问题)

选择题

填空题

C语言内存管理方式

malloc/free

calloc

realloc

C++内存管理方式

new和delete操作内置类型

new和delete操作自定义类型

operator new和operator delete函数

new和delete的实现原理

定位new(placement new)

malloc/free和new/delete的区别

内存泄露


C/C++内存分布

函数调用建立栈帧,去堆上申请一块空间,这个变量存在静态区等等,上述的描述在我们学习语言的过程中应该经常会听到。今天我们站在语言层面上去探索一下C/C++中程序内存的分布(注意:我们所谈论的是在语言层面上关注的几个区域)。

常见区域介绍

1.栈:存非静态局部变量,函数参数,返回值等。栈是向下增长的(高地址到低地址)。

如下图所示的局部变量,返回值等,它们都是一些临时数据,一般存在栈区。可以把它们想象成一次性的物品,只是短暂的使用它完成某项任务。

普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。

2.堆:程序运行时动态内存分配。堆是向上增长的(低地址到高地址)。

比如实现一个动态增长的顺序表,当顺序表满了以后,要进行扩容。每次扩容就要动态的去堆区申请一些空间。常见的操作如malloc、calloc、realloc、C++中更习惯用new来进行空间的动态申请。

3.静态区:存储全局变量和静态数据。如全局变量,static修饰的局部变量等。

被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁所以生命周期变长。

4.常量区:可执行的代码、只读常量等。

经典习题(读代码回答问题)

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在哪里?C(全局变量)     staticVar在哪里?C(静态局部变量)

num1 在哪里?A(静态数组)          staticGlobalVar在哪里?C(静态全局变量)

localVar在哪里?A(局部变量)

第二组:

 char2在哪里?A(数组名)        pChar3在哪里?A(指针变量)  ptr1在哪里?A(指针变量)

*char2在哪里?A(栈区)                *pChar3在哪里?D                        *ptr1在哪里?B

填空题

sizeof(num1) = _40_;          sizeof(ptr1) = _4/8(指针大小)_;
sizeof(char2) = _5(含‘\0’)_;       sizeof(pChar3) = _4/8(指针大小)_; 

strlen(char2) = _4(只求\0前的字符长度)_;      strlen(pChar3) = _4(pChar3指向的字符串长度);

C语言内存管理方式

malloc/free

malloc:向内存申请一块连续可用size个sizeof(数据类型)大小的空间。

free:释放指针指向的空间。

class Date
{
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date* dt = (Date*)malloc(sizeof(Date)*10);
	if (dt == nullptr)
	{
		perror("malloc 失败");
		exit(-1);
	}

	free(dt);
	dt = nullptr;
	return 0;
}

如上图所示,调用malloc函数在堆区动态开辟了3个Date类型成员大小的空间。

calloc

calloc:这个函数也是用来动态开辟内存的,和malloc不同的是,使用calloc函数会在返回地址之前把申请的空间的每个字节初始化为0。

class Date
{
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date* dt = (Date*)calloc(3,sizeof(Date));
	if (dt == nullptr)
	{
		perror("calloc 失败");
		exit(-1);
	}

	free(dt);
	dt = nullptr;
	return 0;
}

calloc在申请空间的同时,还对数据进行了初始化。

realloc

realloc:调整ptr指向的空间大小。

class Date
{
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date* dt = (Date*)malloc(sizeof(Date)*1);

	Date* ptr = (Date*)realloc(dt,sizeof(Date)*5);

	free(ptr);
	dt = ptr = nullptr;
	return 0;
}

原地扩容:如果原空间后的空间足够扩容,则直接在“原地”进行扩容。

异地扩容:在扩容期间可能存在,原空间后的可用空间不够扩容。这时realloc就会采用异地扩容的方式,先找一块连续的空间(扩容后大小),在将原空间中的内容拷贝过来,释放原空间。

了解了realloc的扩容机制,就可以理解在上述代码中,最后只free了ptr。因为如果是原地扩容,dt和ptr指向的是同一块空间,如果是异地扩容,在扩容期间原空间就已经被释放了。

C++内存管理方式

C++中引入了新的内存管理方式,通过new和delete操作符进行动态内存管理。

int main()
{
	//动态申请一个int类型大小的空间
	int* ptr1 = new int;
	//动态申请一个int类型大小的空间,初始化为10
	int* ptr2 = new int(10);
	//动态申请5个int类型的空间
	int* ptr3 = new int[5];
	//动态申请10个int类型的空间
	int* ptr4 = new int[3]{ 0,1,2 };

	delete ptr1;
	delete ptr2;

	delete[] ptr3;
	delete[] ptr4;

	return 0;
}

需要注意的是,new和delete、new[]/delete不要混合使用,否则可能会出现一些错误。 

new和delete操作内置类型

对于内置类型而言,new和malloc只是在语法使用上有些区别。

int main()
{
	int* ptr1 = (int*)malloc(sizeof(int)*5);

	int* ptr2 = new int[5];

	return 0;
}

new和delete操作自定义类型

对于自定义类型而言,除了开空间外,new会调用自定义类型对象的构造函数。delete会调用自定义类型对象的析构函数。malloc与free不会这样做。

class A
{
public:
	A()
	{
		cout << "A()" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
};

int main()
{
	A* a = new A();
	delete a;

	return 0;
}

operator new和operator delete函数

operator new和operator delete是系统提供的全局函数,new在底层调用operator new申请空间,delete在底层通过operator delete释放空间。

●operator new函数实际上是通过malloc来申请空间的。

●operator delete函数实际上是通过free来释放空间的。

new和delete的实现原理

1.对于内置类型来说,new和malloc。delete和free基本类似。

2.new/delete申请和释放的是单个元素的空间,new[]和delete[]申请和释放的是连续的空间。new失败时会抛异常,malloc失败会返回nullptr。

3.对于自定义类型来说,new要做两件事情:

●调用operator new申请空间。

●在申请的空间上执行构造函数,完成对象的构造。(调用自定义类型的构造函数)

4.delete的原理

●调用析构函数,对对象中的资源进行清理。

●调用operator delete函数释放对象的空间。

5.new T[N]的原理

●调用N次operator new完成N个对象空间的申请。

●在每次申请的空间上执行构造函数

6.delete[]

●执行N次析构函数,对N个对象完成资源的清理。

●调用N次operator delete释放空间。

定位new(placement new)

定位new的作用是对已经分配内存的对象调用构造函数初始化一个对象。

语法:new(指针)对象类型(构造传参)

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date* dt = (Date*)malloc(sizeof(Date)*3);

	new(dt)Date(2001, 10, 3);
	new(dt+1)Date(2002,9,9);

	return 0;
}

定位new使用场景:如内存池,内存池中的分配的内存没有初始化。如果是自定义类型对象,需要使用定位new表达式来显示的调用构造函数进行初始化。

malloc/free和new/delete的区别

1.它们的共同点都是在堆上申请空间,并且需要用户显示的去释放。

2.malloc和free是函数,new和delete是操作符。

3.malloc只是完成空间的申请,对于自定义类型而言new还会调用器自身的构造函数,完成对对象的初始化工作。

4.free只是释放空间,对于自定义类型而言delete会代用其自身的析构函数,对对象的资源进行清理。

5.malloc申请空间时,需要手动计算空间大小并传递,new只需要在其后面跟上空间的类型即可,如果是多个对象【】中指定数量。

6.malloc的返回值是void*,在使用对象时必须强转,new不需要,因为new的后面跟的是空间的类型。

7.malloc申请空间失败后,返回的是nullptr,因此使用时必须判空。而new在申请空间后会抛异常,捕获异常即可。

8.对于自定义类型对象的空间申请,malloc和free只会开辟空间。不会调用构造函数和析构函数。

而new在空间申请后,会调用构造函数完成对对象的初始化;

而delete在释放空间前会调用析构函数完成对象空间资源的清理。

内存泄露

内存泄露实际上可以理解为一块空间不在使用了没有释放,且丢掉了能够找到这块空间的指针。内存还在指针丢了。内存泄露并不是物理上的消失,而是失去了对这块空间的控制!!!

1.内存泄露的危害

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

2.内存泄露的分类

堆内存泄露,通过函数调用或者new在堆空间申请空间后,没有被释放。导致这部分空间无法再被使用。

系统资源泄露,文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,可能导致系统不稳定出现一些问题。

3.如何避免内存泄露

预防:编码规范,采用RAII思想,用智能指针来管理资源等。

检测:内存泄露的检测工具。

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

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

相关文章

C++11常用的一部分新特性

C11 统一的列表初始化&#xff5b;&#xff5d;初始化std::initializer_list 声明autodecltypenullptr STL中一些变化新容器已有容器的新接口 右值引用和移动语义左值引用和右值引用右值引用使用场景和意义右值引用引用左值及其一些更深入的使用场景分析完美转发 新的类功能默认…

opencv_c++学习(二十四)

一、积分图像 积分图像是对原图像进行积分操作的算法。如上图左所示&#xff0c;不同颜色代表不同区域。当我们想求取一个像素点的积分值时&#xff0c;我们需要求取该点左上方区域的数据之和&#xff0c;如P0的积分值是浅蓝色区域的数据之和。 P1的积分值为蓝色和橙色区域的数…

网页JS自动化脚本(八)使用网页专属数据库indexedDB进行数据收集

我们在网页上进行的活动,往往都需要进行收集一些简单的数据,但是因为浏览器的安全原因,浏览器基本上是无法与本地的操作系统直接产生数据交互的,这本来就是一个由于安全问题生产的无解问题,在浏览器里面是内置了几种数据库的,其中一种就是indexedDB,可以用来储存一些非常小的数…

C++进阶 —— 线程库(C++11新特性)

十&#xff0c;线程库 thread类的简单介绍 在C11之前涉及多线程问题&#xff0c;都是和平台相关的&#xff0c;如windows和Linux下各有自己的接口&#xff0c;这使代码的可移植性较差&#xff1b;C11中最重要的特性就是对线程进行支持&#xff0c;使得C在并行编程时不需要依赖…

Axure教程—水平方向多色图(中继器)

本文将教大家如何用AXURE制作动态水平方向多色图 一、效果介绍 如图&#xff1a; 预览地址&#xff1a;https://l83ucp.axshare.com 下载地址&#xff1a;https://download.csdn.net/download/weixin_43516258/87822666 二、功能介绍 简单填写中继器内容即可生成动态水平多色…

Linux-模拟一个简单的shell

什么是shell外壳&#xff1f;就是操作系统给我们的一个命令行解释器&#xff0c;在Linux系统中&#xff0c;它的shell叫做bash。 那么bash本质是什么呢&#xff1f; 本质就是一个文件&#xff0c;一个进程。 万物皆文件 每个操作系统的shell都是很复杂的&#xff0c;想要…

【Matter】使用chip tool在ESP32-C3上进行matter开发

文章目录 使用chip tool在ESP32-C3上进行matter开发前提准备编译 chip-tool1.激活esp-matter环境2.编译matter所需环境3.构建CHIP TOOL chip-tool client 调试设备说明1.基于 BLE 调试2.通过IP与设备配对3.Trust store4.忘记当前委托的设备 使用chip-tool点灯1.matter环境激活2…

linuxOPS基础_Linux系统的文件目录结构及用途

linux系统文件目录结构 Linux 系统不同于 Windows&#xff0c;没有 C 盘、D 盘、E 盘那么多的盘符&#xff0c;只有一个根目录&#xff08;/&#xff09;&#xff0c;所有的文件&#xff08;资源&#xff09;都存储在以根目录&#xff08;/&#xff09;为树根的树形目录结构中…

【大数据之Hive】四、配置Hive元数据存储到MySQL

需求&#xff1a;   把Hive元数据写道MySQL的metastore数据库中&#xff08;MySQL默认没有metastore数据库&#xff0c;需要提前创建&#xff1a;create database metastore;&#xff09;   连接地址&#xff1a;jdbc:mysql//hadoop102:3306/metastore   驱动&#xff1a…

什么是SOAP

什么是SOAP 什么是SOAP? SOAP (Simple Object Access Protocol) 是一种基于XML的通信协议&#xff0c;用于在网络上交换结构化的信息。它被广泛用于分布式系统中的应用程序间通信。 SOAP定义了一组规范&#xff0c;描述了消息的格式、通信的方式和处理消息的过程。它允许应…

第四章 程序的控制结构

文章目录 第四章 程序的控制结构4.1 程序的三种控制结构4.1.1 程序流程图4.1.2 程序控制结构基础4.1.3 程序控制结构扩展 4.2 程序的多分支结构4.2.1 单分支结构&#xff1a;if4.2.2 二分支结构&#xff1a;if-else4.2.3 多分支结构&#xff1a;if-elif-else4.2.4 判断条件及组…

图及其与图相关的算法

⭐️前言⭐️ 本篇文章主要介绍图及其与图相关的算法 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主将持续更新学习记录收获&#xff0c;友友们有任何问题可以在评论区留言 &#x1f349;博客中涉及源码及博主…

智慧档案馆八防是怎么建设的?都需要注意哪些内容

智慧档案馆八防环境监控系统一体化解决系统方案 智慧档案库房一体化平台通过智慧档案管理&#xff0c;实现智慧档案感知协同处置功能&#xff1b;实现对档案实体的智能化识别、定位、跟踪监控&#xff1b;实现对档案至智能密集架、空气恒湿净化一体设备、安防设备&#xff0c…

Linux守护进程

守护进程 Linux/Unix 会话 会话首进程 进程组 组长进程&#xff1a;第一个启动的进程叫组长进程。 关闭终端&#xff1a;进程组里全部进程关闭。 setsid()创建一个新的会话。&#xff08;必须是组员进程才可以创建一个新的会话&#xff09; 1.先fork()&#xff0c;退出父进程 2…

电力系统的虚假数据注入攻击和MTD系统研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

字符串最后一个单词的长度

描述 计算字符串最后一个单词的长度&#xff0c;单词以空格隔开&#xff0c;字符串长度小于5000。&#xff08;注&#xff1a;字符串末尾不以空格为结尾&#xff09; 输入描述&#xff1a; 输入一行&#xff0c;代表要计算的字符串&#xff0c;非空&#xff0c;长度小于500…

mysql触发器监听数据投递中间件

目前市面上有许多的 CDC&#xff08;Change Data Capture&#xff09; 框架用于监听数据库的数据变动&#xff0c;例如&#xff1a;canal、Debezium、Maxwell等都是用来解析 binlog 日志实现事件的监听。但是有一个情况就是如果公司对 binlog 日志文件的权限管控的很严格&#…

【学习日记2023.6.2】之 管理端报表统计

文章目录 11. 管理端报表统计11.1 Apache ECharts11.1.1 介绍11.1.2 入门案例 11.2 营业额统计11.2.1 需求分析和设计11.2.2 代码开发Controller层Service层接口Service层实现类Mapper层 11.2.3 功能测试11.2.4 提交代码 11.3 用户统计11.3.1 需求分析和设计11.3.2 代码开发Con…

如何编写接口自动化框架系列通过yaml来管理测试用例(四)

本文是接口自动化测试框架系列篇的第四篇 &#xff0c;主要介绍yaml包的使用 。自动化测试的本质是将功能测试用例交给代码去 目录 1. yaml介绍&#xff1f; 2.python中的yaml包 3.项目中使用yaml包 4 项目总结 执行 &#xff0c;测试人员往往是在自动化框架添加对应的测试…

排查Javascript内存泄漏案例(一)

Chrome DevTools里的Performance面板和Memory面板可以用来定位内存问题。 如何判断应用发生内存泄漏&#xff1f; 为了证明螃蟹的听觉在腿上&#xff0c;一个专家捉了只螃蟹并冲它大吼&#xff0c;螃蟹很快就跑了。然后捉回来再冲它吼&#xff0c;螃蟹又跑了。最后专家把螃蟹的…
最新文章