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

news2024/11/22 15:38:25

目录

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.coloradmin.cn/o/575519.html

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

相关文章

Linux环境变量总结

Linux是一个多用户的操作系统。多用户意味着每个用户登录系统后&#xff0c;都有自己专用的运行环境。而这个环境是由一组变量所定义,这组变量被称为环境变量。用户可以对自己的环境变量进行修改以达到对环境的要求。 设置环境变量的方法 对所有用户生效的永久性变量 这类变…

K8s进阶6——pod安全上下文、Linux Capabilities、OPA Gatekeeper、gvisor

文章目录 一、Pod安全上下文1.1 配置参数1.2 案例11.2.1 dockerfile方式1.2.2 pod安全上下文方式 1.3 案例21.4 Linux Capabilities方案案例1案例2 二、pod安全策略2.1 PSP&#xff08;已废弃&#xff09;2.1.1 安全策略限制维度 2.2 OPA Gatekeeper方案2.2.1 安装Gatekeeper2.…

百度搜索迎来奇点 大模型掀起代际变革

每一轮技术革命掀起的浪潮&#xff0c;大部多数人还没来得及思考或者布局&#xff0c;已经消失于海浪中。机会是给有准备的人的&#xff0c;要发现新兴技术的亮点&#xff0c;并立足自身去积极拥抱它&#xff0c;最后转化为自身前进的动力&#xff0c;跨越周期&#xff0c;迎来…

网站出现403 Forbidden错误的原因以及怎么解决的方法

这几天刚接手一批新做的网站&#xff0c;在访问网站的时候&#xff0c;会时不时的出现403 Forbidden错误&#xff0c;浏览器会给出403 Forbidden错误提示&#xff0c;在打开Access Error中列出的URL之后, 出现以下错误&#xff1a; 403 Forbidden Access to this resource on…

SAP工具箱 批量下载指定表数据到EXCEL

点击蓝字 关注我们 一 前言 下载指定表内容到指定的EXCEL是一个比较简单的程序.但仔细考虑这个程序,还是可以在细节上找出一些关注点 多表内容同时下载,每个表生成一个文件多表选择时,先查看表的记录数大表下载时,拆分下载拆分到不同的文件中拆分到同一个文件中的不同的工作表下…

windows server 2016 ftp搭建详细教程

一.什么是FTP&#xff1f; FTP(File Transfer Protocol)是TCP/IP网络上两台计算机传送文件的协议&#xff0c;使得主机间可以共享文件。 接下来我给大家分享快速搭建FTP服务器的方法。 二.安装FTP服务器 1.进入服务器系统打开“服务器管理器”&#xff0c;点击“添加角色和功…

【JavaSE】Java基础语法(二十三):递归与数组的高级操作

文章目录 1. 递归1.1 递归1.2 递归求阶乘 2. 数组的高级操作2.1 二分查找2.2 冒泡排序2.3 快速排序2.4 Arrays (应用) 1. 递归 1.1 递归 递归的介绍 以编程的角度来看&#xff0c;递归指的是方法定义中调用方法本身的现象把一个复杂的问题层层转化为一个与原问题相似的规模较…

C语言2:说心里话

描述 分两次从控制台接收用户的两个输入&#xff1a;第一个内容为“人名”&#xff0c;第一个内容为“心里 话”。 然后将这两个输入内容组成如下句型并输出出来&#xff1a; 1.(人名&#xff09;&#xff0c;I want to say&#xff0c;(心里话 2. 输入输出示例: 输入&#xff…

MybatisPlus SpringCloud Docker RabbitMQ ElasticSearch、Redis高级技术,分布式事务的综合应用

一、配置SpringCloud中的网关 1. nginx搭建 搭建好了启动nginx.exe即可出静态页面图 1.网关搭建 server:port: 10010 spring:application:name: api-gatewaycloud:nacos:server-addr: localhost:8848gateway:routes: #用户服务的路由&#xff1a;什么样的请求&#xff0c;让网…

Emacs之定制化mode line(第一百零二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…

第二期:链表经典例题(两数相加,删除链表倒数第N个节点,合并两个有序列表)

每道题后都有解析帮助你分析做题&#xff0c;答案在最下面&#xff0c;关注博主每天持续更新。 PS&#xff1a;每道题解题方法不唯一&#xff0c;欢迎讨论&#xff01; 1.两数相加 题目描述 给你两个非空的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照逆序的方式…

【Vue】二:Vue核心处理---模板语法

文章目录 1.模板语法---插值2.模板语法---指令语法2.1v-once2.2 v-bind2.3 v-model2.4 v-on 3.MVVM4.事件回调函数中的this 1.模板语法—插值 {{可以写什么}} &#xff08;1&#xff09;在data中声明的变量&#xff0c;函数 &#xff08;2&#xff09;常量 &#xff08;3&…

【蓝桥杯省赛真题22】python剩余空间问题 青少年组蓝桥杯比赛python编程省赛真题解析

目录 python剩余空间问题 一、题目要求 1、编程实现 二、解题思路

【JavaEE】锁策略、CAS和synchronized的优化

目录 1、常见的锁策略 1.1、乐观锁 vs 悲观锁 1.2、轻量级锁 vs 重量级锁 1.3、自旋锁 vs 挂起等待锁 1.4、互斥锁 vs 读写锁 1.4.1、读写锁的使用场景&#xff08;适用于"频繁 读&#xff0c;不频繁写"的场景&#xff09; 1.5、可重入锁 vs 不可重入锁 1.…

计算机专业学习的核心是什么?

既然是学习CS&#xff0c;那么在这里&#xff0c;我粗浅的把计算机编程领域的知识分为三个部分&#xff1a; 基础知识 特定领域知识 框架和开发技能 基础知识是指不管从事任何方向的软件工程师都应该掌握的&#xff0c;比如数据结构、算法、操作系统。 特定领域知识就是你…

Python花瓣雨

目录 前言 小海龟 花朵类 移动函数 画花朵 尾声 前言 来啦来啦来啦&#xff0c;小伙伴们快快来领取七彩花瓣雨吧&#xff01;&#xff01; 小海龟 老生常谈啦&#xff0c;在用python画樱花树前&#xff0c;我们先来了解一下turtle吧&#xff01; 小海龟(Turtle)是P…

Java学习路线(13)——Collection集合类:List集合与Set集合

一、集合类体系结构 二、部分Collection类型对象 Collection集合特点 List系列集合是有序、可重复、有索引。 ArrayList&#xff1a;有序、可重复、有索引LinkedList&#xff1a;有序、可重复、有索引 Set系列集合是无序、不重复、无索引。 HashSet&#xff1a;无序、不重复…

0202条件过滤-自动装配原理-springboot2.7.x系列

1前言 在springboot的自动装配过程中&#xff0c;执行完候选配置类导入后&#xff0c;会进行条件过滤。那么在讲解条件过滤前&#xff0c;我们先来了解springboot常用的条件注解&#xff0c;以及它们底层执行原理。 在Spring Boot中&#xff0c;条件&#xff08;Condition&am…

使用qemu模拟CXL.mem设备

CXL可以说是自PCIe技术诞生几十年以来最具变革性的新技术了。可以想象有了CXL以后机箱的边界将被彻底打破&#xff0c;服务器互相使用对方的内存&#xff0c;网卡&#xff0c;GPU 。整个机架甚至跨机架的超级资源池化成为可能&#xff0c;云计算也将进入一个新的时代。 当前In…

C++寄存器优化

在C里面有个有趣的现象&#xff0c;先看代码 #include<iostream> using namespace std; int main() {int const tmp 100; //定义常量tmp tmp不能修改int const* p &tmp; //不能通过指针修改指向的值 int* const q const_cast<int*>(p); //去常属性 可以通过…