【C/C++的内存管理】

news2024/11/25 16:54:58

欢迎阅读本篇文章

  • 前言
  • 🍕1. C/C++内存分布
    • 1.1有关C/C++的一道题目
  • 🍕2. C语言中动态内存管理方式:malloc/calloc/realloc/free
  • 🍕3. C++内存管理方式
    • 3.1 new/delete操作内置类型
    • 3.2 new和delete操作自定义类型
  • 🍕4. operator new与operator delete函数(重点)
  • 🍕5. new和delete的实现原理(重点)
    • 5.1 内置类型
    • 5.2 自定义类型
  • 🍕6. 定位new表达式(placement-new) (了解)
  • 🍕7. 常见面试题
  • 🍕8. 内存泄漏
    • 本文开头题目答案


前言

本文章将深入探索C/C++的内存管理模式以及区别。


🍕1. C/C++内存分布

C/C++的内存分布有几大块区域:内核空间,栈区,内存映射段,堆区,数据段,代码段等等。

在这里插入图片描述

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

  2. 填空题:
    sizeof(num1) = ____;
    sizeof(char2) = ____; strlen(char2) = ____;
    sizeof(pChar3) = ____; strlen(pChar3) = ____;
    sizeof(ptr1) = ____;

  3. sizeof 和 strlen 区别?

完成上面题目:将会在文末揭晓答案。

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

在c语言学习到的动态内存管理中,一般会使用到几个函数:malloc/calloc/realloc函数,专门用来向堆区申请空间,而free函数用来归还申请的空间。

面试题:malloc/calloc/realloc函数的区别

malloc:在c语言中,malloc函数是直接向堆区申请空间,如果申请失败,返回NULL,如果申请成功,返回申请到的空间的起始地址,对空间不做处理;

calloc:calloc函数同样是向堆区申请空间,如果申请失败返回NULL,但如果申请成功,不仅会返回申请到的空间的起始地址,还会将该空间进行初始化;

realloc:对于realloc函数,这个函数的申请空间方式有两种:(1)如果是新申请一块空间,其作用跟malloc函数没有区别;(2)如果是需要扩大申请的空间,那么realloc函数会根据内存的容量来做出两个决定的其中一个:1>如果原来的空间后面还有足够的空间满足我们,那么realloc函数就会返回原来申请的空间的地址,但是已经扩大了申请的空间大小,这个叫做原地扩容;2>如果在原来的空间之后没有足够的空间,编译器会在其他地方重新找一块满足要求的空间,并将原空间的内容拷贝到新的空间,并且将原来的空间归还给操作系统,然后返回新的空间的起始地址。

🍕3. C++内存管理方式

C++通过new和delete操作符进行动态内存管理。

3.1 new/delete操作内置类型

int main()
{
	int* pa = new int(10);
	int* parr = new int[10]{0};

	delete pa;
	delete [] parr;
	return 0; 
}

如果要申请一个整型大小的空间,语法:

new 类型(初始化)
小括号内是初始化的内容
返回值为一个指针

如果要申请一个数组大小的空间,语法:

new 类型[数组大小]{初始化内容}
返回值为一个指针

如果要释放一个内置类型,直接

delete 指针名

如果要释放一个数组空间,

delete [] 指针名

3.2 new和delete操作自定义类型

对于自定义类型:

new:申请空间 + 调用构造函数

delete :调用析构函数 + 释放空间

下面来验证结论:

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

int main()
{
	A* a = new A(1);
	
	delete a;
	return 0; 
}

对于new申请自定义类型,会先调用operator new函数,再调用构造函数。
在这里插入图片描述

对于delete,释放自定义类型空间时候,会先调用析构函数,再调用operator delete函数。
在这里插入图片描述

在这里插入图片描述
(新版本的VS编译器将operator delete更进一步地封装了)

在这个过程提到了operaotr delete和operator new两个函数,它们具体是什么,下面会详细地讲述。

🍕4. operator new与operator delete函数(重点)

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数;

new在底层调用operator new全局函数来申请空间;

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

通过operator new 和operator delete 全局函数在库里面的实现可以知道,operator new的实现其实也是调用malloc函数, operator delete的实现也是调用free函数。

🍕5. new和delete的实现原理(重点)

5.1 内置类型

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

5.2 自定义类型

new的原理:
调用operator new函数完成空间的申请,在申请的空间上调用构造函数,完成对象的构造。
delete的原理:
先调用对象的析构函数完成对象内资源的清理工作,再调用operator delete函数完成对对象空间的释放
new T[N]的原理:
调用operator new[]函数,再operator new[]函数中调用N次operator new函数完成空间的申请, 并且每次调用operator new后会调用对象的构造函数完成对象的构造。
operator delete T[N]的原理:
先调用多次析构函数对对象内资源的空间进行释放,再调用operator delete[]函数,其中operator delete[]内又多次调用operator delete函数对对象的资源进行释放。

下面给一个例题感受一下:

class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	
	void Push(const DataType& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
	
private:
	DataType *_array;
	size_t _size;
	size_t _capacity;
};

在对栈的操作时,首先调用operator new函数申请一块空间作为栈对象的空间。其次调用构造函数,完成对象内数组的申请。
在资源释放时,会先调用析构函数,释放对象内的数组空间,然后在调用operator delete函数释放对象的空间。

在这里插入图片描述

🍕6. 定位new表达式(placement-new) (了解)

定位new表达式是对一个未初始化的对象显式地调用构造函数。
这是new关键字的另一种玩法。

使用方法:

new(对象指针)+类名

int main()
{
// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
A* p1 = (A*)malloc(sizeof(A));
new(p1)A;  // 注意:如果A类的构造函数有参数时,此处需要传参
p1->~A();
free(p1);

这个就是定位new的用法。
定位new通常与内存池配合使用,因为向内存池申请的空间不会初始化,如果是自定义类型,需要显式地调用构造函数进行初始化

🍕7. 常见面试题

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

🍕8. 内存泄漏

内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

平常时一定要注意内存泄露的问题,申请的资源空间一定要及时释放。

本文开头题目答案

在这里插入图片描述

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

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

相关文章

论国内如何免费使用GPT4

什么是GPT&#xff0c;能做什么&#xff1f; GPT&#xff0c;全名为Generative Pre-trained Transformer&#xff0c;是一类基于Transformer架构的自然语言处理模型。GPT的主要功能包括&#xff1a; 文本生成&#xff1a;能够根据给定的输入生成合理的文本&#xff0c;如文章、…

双向链表实现约瑟夫问题

title: 双向链表实现约瑟夫问题 date: 2023-05-16 11:42:26 tags: **问题&#xff1a;**知n个人围坐在一张圆桌周围。从编号为k的人开始报数&#xff0c;数到m的那个人出列&#xff1b;他的下一个人又从1开始报数&#xff0c;数到m的那个人又出列&#xff1b;依此规律重复下去&…

Java进阶-Collection集合

1.Collection集合 1.1数组和集合的区别 相同点 都是容器&#xff0c;可以存储多个数据 不同点 数组的长度是不可变的&#xff0c;集合的长度是可变的 数组可以存基本数据类型和引用数据类型 集合只能存引用数据类型&#xff0c;如果要存基本数据类型&#xff0c;需要存对应的…

ubuntu20.04开机界面黑屏,只有一个光标闪烁

接下来我就把我的解决方法完整的发出来&#xff0c;因为我也是非常的绝望&#xff0c;终于在不断尝试中解决了问题 首先开机界面就是这个东西&#xff0c;一直卡在这不动了&#xff0c;原因就是&#xff0c;内存被用完了&#xff0c;无法加载出图形化界面 解决方法&#xff1…

springboot基于vue的MOBA类游戏攻略分享平台

系统分析 系统可行性分析 1、经济可行性 由于本系统本身存在一些技术层面的缺陷&#xff0c;并不能直接用于商业用途&#xff0c;只想要通过该系统的开发提高自身学术水平&#xff0c;不需要特定服务器等额外花费。所有创造及工作过程仅需在个人电脑上就能实现&#xff0c;使…

Redis学习--下载与安装

Redis下载与安装 Redis安装包分为windows版和Linux版&#xff1a; Windows版下载地址&#xff1a;https://github.com/microsoftarchive/redis/releases Linux版下载地址&#xff1a;https:/download.redis.io/releases 在Linux系统安装Redis步骤&#xff1a; 1.将Redis安装…

JENKINS部署-学习踩坑日记

1、JENKINS情况介绍 使用docker安装JENKINS&#xff0c;教程可以在网上搜到&#xff0c;步骤执行&#xff1b; 2、服务器情况介绍 JENKINS部署在A服务器上面&#xff0c;要把项目从gitlab上面拉取下来&#xff0c;然后编译推送jar到B服务器&#xff0c;然后通过docker-compose…

Linux:文本三剑客之sed编辑器

Linux&#xff1a;sed编辑器 一、sed1.1 sed编辑器1.2 sed编辑器的工作流程1.3 命令格式1.4常用选项1.5 常用操作1.6 实际应用 一、sed 1.1 sed编辑器 sed是一种流编辑器&#xff0c;流编辑器会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。sed编辑器可以根据命…

理解JVM

认识JVM Java 虚拟机&#xff08;JVM&#xff09;是运行 Java 字节码的虚拟机。 什么是字节码&#xff1f; 字节码就是jvm能理解的代码。即扩展名为 .class 的文件。 我们日常的java文件先编译成.class 文件 然后在jvm上运行。 个人觉得 内存区域是理解JVM相关的基石。所以彻…

微服务简介,SpringCloud Alibaba Nacos的安装部署与使用,Nacos集成springboot

目录 一.认识微服务 1.0.学习目标 1.1.单体架构 单体架构的优缺点如下&#xff1a; 1.2.分布式架构 分布式架构的优缺点&#xff1a; 1.3.微服务 微服务的架构特征&#xff1a; 1.4.SpringCloud 1.5Nacos注册中心 1.6.总结 二、Nacos基本使用 &#xff08;一&…

【C++】深入剖析C++11新特性

目录 一、C11简介 二、统一的列表初始化 1.&#xff5b;&#xff5d;初始化 2.std::initializer_list 三、声明 1.auto 2.decltype 3.nullptr 四、范围for 五、final和oberride 六、STL中一些变化 1.array 2.forward_list 3.unordered_map和unordered_set 七、右…

RabbitMQ养成记 (2. java操作MQ快速入门,日志监控,消息追踪)

快速入门 刚开始我们就一步一步来&#xff0c; 先搞什么spring集成。 先使用原始的java代码来操作一下MQ。 这里给新手兄弟的建议&#xff0c;这种技术性的学习 一定要先动手&#xff0c;从简单的地方动手&#xff0c;一步一步来&#xff0c;不然上来就搞理论或者复杂的应用很…

JDBC API

注册数据库驱动 Class.forName("com.mysql.jdbc.Driver"); 所谓的注册驱动&#xff0c;就是让JDBC程序加载mysql驱动程序&#xff0c;并管理驱动 驱动程序实现了JDBC API定义的接口以及和数据库服务器交互的功能&#xff0c;加载驱动是为了方便使用这些功能。 获…

Spring IOC相关注解运用——下篇

目录 一、Configuration 二、ComponentScan 1. 说明 2. 测试方法 3. 运行结果 三、PropertySource 1. 说明 2. 测试方法 3. 测试结果 四、Bean 1. 说明 2. 添加驱动依赖 3. 将Connection对象放入Spring容器 3. 测试 五、Import 1. 说明 2. 测试方法 3. 运行结…

从一道go逆向出发,讨论类tea的逆算法

tea代码很短&#xff0c;经常被直接复制为源码&#xff08;而不是像标准算法那样调库&#xff09;。在ctf逆向中也算比较常见&#xff0c;复杂度适中。 例题是一道go逆向&#xff0c;经go parser处理后&#xff0c;核心代码如下图。 panic算是go的专有名词&#xff0c;类似异常…

吃透 Spring AOP (1.理解概念)

理解 什么是AOP AOP&#xff0c;全称面向切面编程。 它可以说是对面向对象OOP的思想升华。从总的理解来讲&#xff0c;AOP是横向对不同程序的抽象。这个思想要不断实践动手之后&#xff0c;才会有很深刻的理解 理解 代理模式 在理解AOP之前&#xff0c;我们首先要单独说一个…

FFMPEG录屏(16)--- MAG(Magnification)捕获桌面

最近增加了对Magnification API捕获桌面的支持&#xff0c;记录一下过程和其中遇到的问题。 参考资料 Magnification API overview Magnification API sample webrtc screen_capturer_win_magnifier.cc Structured Exception Handling (C/C) 前言 我又不得不吐槽一下了&a…

【JavaWeb】-- HTTP、Tomcat、Servlet

文章目录 HTTP1.简介2.请求数据格式2.2.1 格式介绍 3.响应数据格式3.1 格式介绍3.2 响应状态码 Tomcat1.简介1.1 什么是Web服务器 2.基本使用2.1 安装2.2 启动2.3 关闭 3.Maven创建Web项目4.IDEA使用Tomcat4.1集成本地Tomcat4.2 Tomcat Maven插件 Servlet1.简介2.快速入门3.执行…

okhttp篇3:RealCall

Call Call一般代表一个已经准备好的Request&#xff0c;Request的包装类&#xff0c;可执行&#xff0c;它一般有两个主要的方法&#xff1a; execute(立即执行&#xff0c;并阻塞线程&#xff0c;直到Response返回)enqueue(将Request放入队列&#xff0c;等待线程池调度执行…

spring源码学习_01 本地环境搭建

参考网上各种资源&#xff0c;终于把spring源码运行起来了&#xff1b;步骤总结如下&#xff1a; spring版本&#xff1a; 5.2.x 本地系统mac idea 2019.3.2 下载地址&#xff1a;https://www.jetbrains.com/idea/download/other.html jdk 11 下载地址&#xff1a;https://repo…