C/C++——内存管理

news2024/11/17 21:36:50

1.为什么存在动态内存分配

灵活性

静态内存分配是在编译时确定的,程序执行过程中无法改变所分配的内存大小;动态内存分配可以根本程序的运行环境来动态分配和释放空间,提供了更大的灵活性

动态数据结构

有些数据结构的大小和结构在编译时无法确定,需要在运行通过动态内存分配来创建和操作

资源的高效利用

通过动态内存分配,程序可以根据实际需求分配合适的内存空间,避免了静态内存分配中可能出现的浪费内存的问题。动态内存分配可以提高内存的利用率,允许多个对象共享内存空间。

编译灵活性

可以根据程序的运行状态来决定内存的分配和释放,使得程序具有更大的灵活性。

2.C/C++内存分布

请添加图片描述

栈区(stack)

在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内容容量有限,栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等

内存映射段

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

堆区(heap)

一般由程序员分配释放,若程序员不释放,程结束时可能由OS回收,分配方式类似于链表

数据段(静态区)

存放全局变量、静态变量。程序结束后由系统释放

代码段

存放函数体(类成员函数和全局函数)的二进制

3.C语言内存管理方式

3.1malloc和free

函数原型如下:

void* malloc (size_t size);

malloc函数向内存申请一块连续可用的空间,并返回指向这块空间的指针

  • 如果开辟成功,则返回一个指向开辟好空间的指针
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查
  • 返回值的类型是void*,所有malloc函数并不知道开辟空间的类型,具体在使用者自己来决定
  • 如果参数size为0,malloc的行为是标准还是未定义的,取决于编译器
void free (void* ptr);

free函数用来释放动态开辟的内存

  • 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的
  • 如果参数ptr是NULL指针,则函数什么事情都不做
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int num = 10;
	int* ptr = NULL;
	ptr = (int*)malloc(num * sizeof(int));//动态开辟10个int类型的内存空间
	if (NULL != ptr)						//判断ptr是否为空,
	{
		int i = 0;
		for (i = 0; i < num; i++)
		{
			*(ptr + i) = 0;
			printf("%d ", *ptr);
		}
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

3.2calloc

函数原型如下:

void* calloc (size_t num, size_t size);
  • 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0
  • 与函数malloc的区别只在于calloc会有返回地址之前把之前申请的空间的每个字节初始化为全0
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (NULL != p)
	{
		for (int i = 0; i <10; i++)
		{
			*(p + i) = i+1;
			printf("%d ", *(p+i));
		}
	}
	free(p);
	p = NULL;
	return 0;
}

3.3realloc

realloc函数的出现让动态内存管理更加灵活,可以做到对动态开辟内存大小的调整。

函数原型如下:

void* realloc (void* ptr, size_t size);
  • ptr是要调整的内存地址
  • size调整之后新大小
  • 返回值为调整之后的内存起始地址
  • 这个函数调整原内存空间大小的基础上,还会将原来的内存中的数据移动到新的空间

realloc在调整内存空间存在的两种情况:

1.原空间之后有足够大的空间——要扩展空间就直接在原有内存之后直接追加空间,原来空间的数据不发生变化

2.原空间之后没有足够大的空间——原空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)malloc(5 * sizeof(int)); // 分配5个整型大小的内存块
    if (p == NULL) {
        printf("Memory allocation failed.\n");
        return -1;
    }

    for (int i = 0; i < 5; i++) {
        p[i] = i + 1;  // 给每个元素赋值
        printf("%d ", p[i]);
    }
    printf("\n");

    int* new_p = (int*)realloc(p, 10 * sizeof(int)); // 将内存块大小重新分配为10个整型大小

    if (new_p == NULL) {
        printf("Memory reallocation failed.\n");
        free(p); // 重新分配失败,释放原先分配的内存块
        return -1;
    }

    for (int i = 0; i < 10; i++) {
        new_p[i] = i + 1;  // 给每个元素赋值
        printf("%d ", new_p[i]);
    }
    printf("\n");

    free(new_p); // 释放重新分配的内存块
    new_p = NULL;

    return 0;
}

4.C++内存管理方式

4.1new和delete操作内置类型

申请和释放单个元素的空间,使用newdelete操作符,申请和释放连续的空间,使用new[]delete[](注意:匹配起来使用)

void Test()
{
    // 动态申请一个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;
}

4.2operator new和operator delete函数

newdelete是用户进行动态内存申请和释放的操作符,operator newoperator 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;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

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

4.3new和delete的实现原理

4.3.1内置类型

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

4.3.2自定义类型

  • new的原理
    • 调用operator new函数申请空间
    • 在申请的空间上执行构造函数,完成对象的构造
  • delete的原理
    • 在空间上执行析构函数,完成对象中资源的清理工作
    • 调用operator delete函数释放对象的空间
  • new T[N]的原理
    • 调用operator new[]函数,在operator new[]实际调用operator new函数完成N个对象空间的申请
    • 在申请的空间上执行N此构造函数
  • delete[]的原理
    • 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
    • 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

5.malloc/free和new/delete的区别

5.1共同点

都是从堆上申请空间,并且需要用户手动释放

5.2不同点

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

6.常见的动态内存错误

6.1对NULL指针的解引用操作

//c
void test()
{
	int *p = (int *)malloc(INT_MAX/4);
	*p = 20;//如果p的值是NULL,就会有问题
	free(p);
}
//c++
void test()
{
	int *p = new int(INT_MAX/4);
	*p = 20;//如果p的值是NULL,就会有问题
	delete(p)
}

6.2对动态开辟空间的越界访问

//c
void test()
{
    int i = 0;
    int *p = (int *)malloc(10*sizeof(int));
    if(NULL == p)
    {
    	exit(EXIT_FAILURE);
    }
    for(i=0; i<=10; i++)
    {
    	*(p+i) = i;//当i是10的时候越界访问
    }
    free(p);
}
//c++
void test()
{
    int i = 0;
    int* p = new int[10];
    if (nullptr == p)
    {
        exit(EXIT_FAILURE);
    }
    for (i = 0; i <= 10; i++)
    {
        *(p + i) = i; // 当i是10的时候越界访问
    }
    delete[] p;
}

6.3对非动态开辟内存进行释放

//c
void test()
{
    int a = 10;
    int *p = &a;
    free(p);
}
//c++
void test()
{
    int a = 10;
    int *p = &a;
    delete p;
}

6.4释放一块动态开辟内存的一部分

//c
void test()
{
    int *p = (int *)malloc(100);
    p++;
    free(p);//p不再指向动态内存的起始位置
}
//c++
void test()
{
    int* p = new int[100];
    p++;
    delete[] p; //p不再指向动态内存的起始位置
}

6.5对同一块动态内存多次释放

//c
void test()
{
    int *p = (int *)malloc(100);
    free(p);
    free(p);//重复释放
}
//c++
void test()
{
    int *p = (int *)malloc(100);
    delete[] p;
    delete[] p;//重复释放
}

6.6动态开辟内存忘记释放(内存泄漏)

//c
int* p1 = (int*)malloc(sizeof(int));
//c++
int* p2 = new int;

6.6.1什么是内存泄漏?

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内
存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对
该段内存的控制,因而造成了内存的浪费 。

6.6.2内存泄漏的危害?

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

6.6.3如何避免内存泄漏?

  1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智能指针来管理才有保证。
  2. 采用RAII思想或者智能指针来管理资源。
  3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
  4. 出问题了使用内存泄漏工具检测。不过很多工具都不够靠谱,或者收费昂贵。

6.6.1什么是内存泄漏?

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内
存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对
该段内存的控制,因而造成了内存的浪费 。

6.6.2内存泄漏的危害?

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

6.6.3如何避免内存泄漏?

  1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智能指针来管理才有保证。
  2. 采用RAII思想或者智能指针来管理资源。
  3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
  4. 出问题了使用内存泄漏工具检测。不过很多工具都不够靠谱,或者收费昂贵。

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

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

相关文章

input输入多行文本:删除“首先 其次 此外 总的来说”

input允许多行输入 233.3表示停止输入input输入多行文本文本 &#xff08;空行&#xff09; &#xff08;空行&#xff09; &#xff08;空行&#xff09; 正文 &#xff08;空行&#xff09; &#xff08;空行&#xff09; &#xff08;空行&#xff09; 正文 &#xff08;空行…

国庆节:不仅仅是庆祝,更是成长与体验

目录 国庆节&#xff1a;不仅仅是庆祝&#xff0c;更是成长与体验引言第一部分&#xff1a;旅途风景目的地选择旅行亮点与国庆的联系 技术主题完成的博文国庆与技术 第三部分&#xff1a;回家的路为什么回家艰难险阻家与国庆 结论 国庆节&#xff1a;不仅仅是庆祝&#xff0c;更…

【Spring笔记02】Spring中的IOC容器和DI依赖注入介绍

这篇文章&#xff0c;主要介绍一下Spring中的IOC容器和DI依赖注入两个概念。 目录 一、IOC控制反转 1.1、什么是IOC 1.2、两种IOC容器 &#xff08;1&#xff09;基于BeanFactory的IOC容器 &#xff08;2&#xff09;基于ApplicationContext的IOC容器 二、DI依赖注入 2.…

Vue MVVM 模型

一、什么事MVVM 模型 MVVM 是 Model-View-ViewModel 的缩写&#xff0c;它是一种软件架构风格 Model&#xff1a;模型&#xff0c; 数据对象&#xff08;data 函数&#xff09;&#xff0c;如下图 View&#xff1a;视图&#xff0c;模板页面&#xff08;用于渲染数据&#xf…

掌握Mac菜单栏,尽在Bartender 5!菜单栏图标管理软件的终极推荐!

作为Mac用户&#xff0c;菜单栏是我们每天使用电脑时最常接触的区域之一。然而&#xff0c;随着我们安装越来越多的应用程序&#xff0c;菜单栏上的图标往往变得拥挤不堪&#xff0c;给我们的工作和生活带来了不便。 幸运的是&#xff0c;有了Bartender 5这款强大的菜单栏图标…

数据结构与算法(Python)

数据结构与算法 算法基础时间复杂度空间复杂度 递归实例&#xff1a;汉诺塔问题 查找顺序查找&#xff08;线性查找&#xff09;二分查找&#xff08;折半查找&#xff09;比较 排序冒泡排序选择排序插入排序快速排序快排和冒泡的时间比较 堆排序树堆堆的向下调整 堆排序过程时…

除静电设备的工作原理及应用

除静电设备主要包括静电消除器、静电接地装置、静电消除风机等&#xff0c;它们的工作原理和应用如下&#xff1a; 静电消除器&#xff1a;静电消除器的工作原理是利用电离和电击的原理来中和电荷。它包括一个金属板和一个高压电源。当静电消除器接通电源后&#xff0c;金属板…

Redis最常见应用场景

缓存&#xff08;Cache&#xff09; Redis的第一个应用场景是Redis作为缓存对象来加速Web应用的访问。 在该场景下&#xff0c;有一些存储于数据库中的数据会被频繁访问&#xff0c;如果频繁的访问数据库&#xff0c;数据库负载会升高&#xff0c;同时由于数据库IO比较慢&…

阿里云服务器更换公网IP地址的方法流程

阿里云服务器可以更换IP地址吗&#xff1f;可以的&#xff0c;创建6小时以内的云服务器ECS可以免费更换三次公网IP地址&#xff0c;超过6小时的云服务器&#xff0c;可以将公网固定IP地址转成弹性EIP&#xff0c;然后通过换绑EIP的方式来更换IP地址。阿里云服务器网分享阿里云服…

阿里云服务器地域节点怎么选择合适?啥是可用区?

阿里云服务器地域和可用区怎么选择&#xff1f;地域是指云服务器所在物理数据中心的位置&#xff0c;地域选择就近选择&#xff0c;访客距离地域所在城市越近网络延迟越低&#xff0c;速度就越快&#xff1b;可用区是指同一个地域下&#xff0c;网络和电力相互独立的区域&#…

基于遗传算法的新能源电动汽车充电桩与路径选择(Matlab代码实现)

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

JavaScript系列从入门到精通系列第十六篇:JavaScript使用函数作为属性以及枚举对象中的属性

文章目录 前言 1&#xff1a;对象属性可以是函数 2&#xff1a;对象属性函数被称为方法 一&#xff1a;枚举对象中的属性 1&#xff1a;for...in 枚举对象中的属性 前言 1&#xff1a;对象属性可以是函数 对象的属性值可以是任何的数据类型&#xff0c;也可以是函数。 v…

RHEL - 订阅、注册系统和 Yum Repository

《OpenShift / RHEL / DevSecOps 汇总目录》 演示环境说明 本文需要有 redhat.com 账号以及包含 RHEL 的有效订阅。 演示环境使用了通过 minimal 方式安装的 RHEL 7.6 环境&#xff0c;RHEL 可以访问互联网。 注册和注销 RHEL 系统 在 RHEL 中执行以下命令查看当前 RHEL 版…

第P8周—YOLOv5-C3模块实现

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/Nb93582M_5usednAKp_Jtw) 中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊 | 接辅导、项目定制](https://mtyjkh.blog.csdn.net/)** >- **&#x1f680;…

transformer不同的包加载模型的结构不一样

AutoModel AutoModelForTokenClassification 结论&#xff1a; AutoModel加载的模型与AutoModelForTokenClassification最后一层是不一样的&#xff0c;从这个模型来看&#xff0c;AutoModelForTokenClassification加载的结果是对的 问题&#xff1a; 为什么AutoModel和Aut…

网络探索之浏览器解析URL

目录 解析URL 特殊情况&#xff1a; 请求响应格式说明 多次请求 解析URL 浏览器是一个具备很多功能的计算机&#xff0c;不仅仅是访问网络浏览信息&#xff0c;使用具体哪个功能是通过url的开头进行区分的&#xff0c;这部分也叫做协议。 协议类型举例&#xff1a; 1.使用…

Windowsold文件夹作用以及删除方法

引言 2021年6月24日&#xff0c;微软正式发布全新操作系统Windows 11。Windows 11系统于2021年10月5日开始全面推送。2021年10月以后生产的电脑已经预装Windows 11系统。刚开始会有一部分人不适应win 11系统&#xff0c;会选择退回win10。现在win11已经推出了稳定版&#xff0…

Gmail 将停止支持基本 HTML 视图

根据 Google 支持文档的更新内容&#xff0c;Gmail 将从明年 1 月起停止支持基本 HTML 视图。 ▲ Gmai 基本 HTML 视图界面 目前网页版 Gmail 提供两个界面&#xff1a;基本 HTML 视图和标准视图。停止支持基本 HTML 视图后&#xff0c;当前打开经典模式的基本 HTML 视图模式 …

【Pandas】数据分组groupby

本文目标&#xff1a; 应用groupby 进行分组对分组数据进行聚合,转换和过滤应用自定义函数处理分组之后的数据 文章目录 1. 数据聚合1.1 单变量分组聚合1.2 Pandas内置聚合方法1.3 聚合方法使用Numpy的聚合方法自定义方法同时计算多种特征向agg/aggregate传入字典 2. 数据转换…

好文学作品的鉴赏标准

好文学作品的鉴赏标准 2023年诺贝尔文学奖颁给了挪威剧作家约恩福瑟。由于之前的博彩公司给中国作家残雪开出了最高的赔率&#xff0c;以及诺贝尔官方推特在揭晓奖项前发布了一张泰戈尔99年前访华的老照片&#xff0c;残雪的获奖氛围在国内各类媒体的渲染下被拉至极高。当奖项…